포터-더프 블렌드 모드

포터-더프 블렌드 모드는 루카스필름에서 일하는 동안 작곡의 대수를 개발한 토마스 포터와 톰 더프의 이름을 따서 명명되었습니다. 디지털 이미지 작성 논문은 1984년 7월호 컴퓨터 그래픽 253-259페이지에 실렸습니다. 이러한 혼합 모드는 다양한 이미지를 복합 장면으로 어셈블하는 작성에 필수적입니다.

포터 더프 샘플

포터 더프 개념

갈색 사각형이 디스플레이 화면의 왼쪽 및 상위 3분의 2를 차지한다고 가정합니다.

포터 더프 목적지

이 영역을 대상 또는 배경 또는 배경이라고합니다.

대상과 동일한 크기의 다음 사각형을 그리려고 합니다. 직사각형은 오른쪽 및 아래쪽 3분의 2를 차지하는 푸르른 영역을 제외하고 투명합니다.

포터 더프 소스

이를 원본 또는 전경이라고도 합니다.

대상에 원본을 표시할 때 예상되는 항목은 다음과 같습니다.

포터 더프 소스 오버

원본의 투명한 픽셀을 사용하면 배경을 표시할 수 있는 반면, 블러쉬 소스 픽셀은 배경을 가릴 수 있습니다. 이것이 일반적인 경우이며 SkiaSharp에서 다음과 같이 SKBlendMode.SrcOver언급됩니다. 이 값은 개체가 BlendMode 처음 인스턴스화될 때 SKPaint 속성의 기본 설정입니다.

그러나 다른 효과에 대해 다른 혼합 모드를 지정할 수 있습니다. 를 지정 SKBlendMode.DstOver하면 원본과 대상이 교차하는 영역에서 원본 대신 대상이 나타납니다.

포터 더프 목적지 오버

혼합 모드는 SKBlendMode.DstIn 대상 색을 사용하여 대상과 소스가 교차하는 영역만 표시합니다.

포터 더프 목적지

(전용 OR)의 SKBlendMode.Xor 혼합 모드는 두 영역이 겹치는 곳에 아무 것도 나타나지 않습니다.

포터 더프 배타적 또는

색이 지정된 대상 및 원본 사각형은 표시 표면을 대상 및 원본 사각형의 존재에 해당하는 다양한 방법으로 색을 지정할 수 있는 네 개의 고유한 영역으로 효과적으로 나눕니다.

포터 더프

대상과 원본이 모두 해당 영역에서 투명하기 때문에 오른쪽 위와 왼쪽 아래 사각형은 항상 비어 있습니다. 대상 색은 왼쪽 위 영역을 차지하므로 해당 영역은 대상 색으로 색을 지정하거나 전혀 색을 지정할 수 없습니다. 마찬가지로 원본 색은 오른쪽 아래 영역을 차지하므로 해당 영역은 원본 색으로 색을 지정할 수도 있고 전혀 색이 지정되지 않을 수도 있습니다. 가운데에 있는 대상과 원본의 교차점은 대상 색, 원본 색 또는 전혀 색이 지정되지 않을 수 있습니다.

총 조합 수는 2번(왼쪽 위) 2번(오른쪽 아래의 경우) 3번(가운데) 또는 12번입니다. 이들은 12 기본 포터 더프 작곡 모드입니다.

디지털 이미지 작성(256페이지)이 끝날 무렵, 포터와 더프는 더하기(SkiaSharp SKBlendMode.Plus 멤버 및 W3C 라이터 모드에 해당)라는 13번째 모드를 추가합니다(W3C Lighten 모드와 혼동해서는 안 됩니다.) 이 Plus 모드는 대상 및 원본 색을 추가합니다. 이 프로세스는 곧 더 자세히 설명될 것입니다.

Skia는 대상 및 원본 색을 곱한다는 점을 제외하고 매우 유사한 Plus 14번째 모드를 Modulate 추가합니다. 추가 포터-더프 블렌드 모드로 처리할 수 있습니다.

다음은 SkiaSharp에 정의된 14개의 포터-더프 모드입니다. 이 표에서는 위 다이어그램에서 비어 있는 세 개의 비어 있는 영역 각각에 색을 지정하는 방법을 보여줍니다.

모드 대상 교차로 Source
Clear
Src Source X
Dst X 대상
SrcOver X Source X
DstOver X 대상 X
SrcIn 원본
DstIn 대상
SrcOut X
DstOut X
SrcATop X 원본
DstATop 대상 X
Xor X X
Plus X Sum X
Modulate Product

이러한 혼합 모드는 대칭입니다. 원본 및 대상을 교환할 수 있으며 모든 모드를 계속 사용할 수 있습니다.

모드의 명명 규칙은 다음과 같은 몇 가지 간단한 규칙을 따릅니다.

  • Src 또는 Dst 자체는 원본 또는 대상 픽셀만 표시됨을 의미합니다.
  • Over 접미사는 교집합에 표시되는 내용을 나타냅니다. 원본 또는 대상이 다른 원본 또는 대상에 "위에" 그려집니다.
  • In 접미사는 교차점만 색이 지정됨을 의미합니다. 출력은 다른 원본 또는 대상의 일부만 "in"으로 제한됩니다.
  • Out 접미사는 교차점이 색이 지정되지 않음을 의미합니다. 출력은 교집합에서 "out"인 원본 또는 대상의 일부일 뿐입니다.
  • ATop 접미사는 InOut통합입니다. 여기에는 원본 또는 대상이 다른 원본의 "맨 위에" 있는 영역이 포함됩니다.

모드와 Modulate 모드의 차이점을 Plus 확인합니다. 이러한 모드는 원본 및 대상 픽셀에 대해 다른 유형의 계산을 수행합니다. 그들은 곧 더 자세히 설명되어 있습니다.

Porter-Duff Grid 페이지에는 그리드 형태로 한 화면에 14개의 모드가 모두 표시됩니다. 각 모드는 별도의 .의 인스턴스입니다 SKCanvasView. 이러한 이유로 클래스는 명명PorterDuffCanvasView된 에서 SKCanvasView 파생됩니다. 정적 생성자는 동일한 크기의 두 비트맵을 만듭니다. 하나는 왼쪽 위 영역에 갈색 사각형이 있고 다른 하나는 흐린 사각형이 있는 비트맵입니다.

class PorterDuffCanvasView : SKCanvasView
{
    static SKBitmap srcBitmap, dstBitmap;

    static PorterDuffCanvasView()
    {
        dstBitmap = new SKBitmap(300, 300);
        srcBitmap = new SKBitmap(300, 300);

        using (SKPaint paint = new SKPaint())
        {
            using (SKCanvas canvas = new SKCanvas(dstBitmap))
            {
                canvas.Clear();
                paint.Color = new SKColor(0xC0, 0x80, 0x00);
                canvas.DrawRect(new SKRect(0, 0, 200, 200), paint);
            }
            using (SKCanvas canvas = new SKCanvas(srcBitmap))
            {
                canvas.Clear();
                paint.Color = new SKColor(0x00, 0x80, 0xC0);
                canvas.DrawRect(new SKRect(100, 100, 300, 300), paint);
            }
        }
    }
    ···
}

인스턴스 생성자에는 형식의 매개 변수가 있습니다 SKBlendMode. 이 매개 변수는 필드에 저장됩니다.

class PorterDuffCanvasView : SKCanvasView
{
    ···
    SKBlendMode blendMode;

    public PorterDuffCanvasView(SKBlendMode blendMode)
    {
        this.blendMode = blendMode;
    }

    protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Find largest square that fits
        float rectSize = Math.Min(info.Width, info.Height);
        float x = (info.Width - rectSize) / 2;
        float y = (info.Height - rectSize) / 2;
        SKRect rect = new SKRect(x, y, x + rectSize, y + rectSize);

        // Draw destination bitmap
        canvas.DrawBitmap(dstBitmap, rect);

        // Draw source bitmap
        using (SKPaint paint = new SKPaint())
        {
            paint.BlendMode = blendMode;
            canvas.DrawBitmap(srcBitmap, rect, paint);
        }

        // Draw outline
        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.Color = SKColors.Black;
            paint.StrokeWidth = 2;
            rect.Inflate(-1, -1);
            canvas.DrawRect(rect, paint);
        }
    }
}

재정의는 OnPaintSurface 두 비트맵을 그립니다. 첫 번째는 정상적으로 그려집니다.

canvas.DrawBitmap(dstBitmap, rect);

두 번째는 속성이 BlendMode 생성자 인수로 설정된 개체로 그려집니다SKPaint.

using (SKPaint paint = new SKPaint())
{
    paint.BlendMode = blendMode;
    canvas.DrawBitmap(srcBitmap, rect, paint);
}

재정의 OnPaintSurface 의 re기본der는 비트맵 주위에 사각형을 그려 크기를 나타냅니다.

클래스는 PorterDuffGridPage 배열의 PorterDurffCanvasView각 멤버에 대해 하나씩 14개의 인스턴스를 blendModes 만듭니다. 배열의 SKBlendModes 멤버 순서는 비슷한 모드를 서로 인접하게 배치하기 위해 테이블과 약간 다릅니다. 14개의 인스턴스는 다음의 PorterDuffCanvasView 레이블과 함께 구성됩니다 Grid.

public class PorterDuffGridPage : ContentPage
{
    public PorterDuffGridPage()
    {
        Title = "Porter-Duff Grid";

        SKBlendMode[] blendModes =
        {
            SKBlendMode.Src, SKBlendMode.Dst, SKBlendMode.SrcOver, SKBlendMode.DstOver,
            SKBlendMode.SrcIn, SKBlendMode.DstIn, SKBlendMode.SrcOut, SKBlendMode.DstOut,
            SKBlendMode.SrcATop, SKBlendMode.DstATop, SKBlendMode.Xor, SKBlendMode.Plus,
            SKBlendMode.Modulate, SKBlendMode.Clear
        };

        Grid grid = new Grid
        {
            Margin = new Thickness(5)
        };

        for (int row = 0; row < 4; row++)
        {
            grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Star });
        }

        for (int col = 0; col < 3; col++)
        {
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star });
        }

        for (int i = 0; i < blendModes.Length; i++)
        {
            SKBlendMode blendMode = blendModes[i];
            int row = 2 * (i / 4);
            int col = i % 4;

            Label label = new Label
            {
                Text = blendMode.ToString(),
                HorizontalTextAlignment = TextAlignment.Center
            };
            Grid.SetRow(label, row);
            Grid.SetColumn(label, col);
            grid.Children.Add(label);

            PorterDuffCanvasView canvasView = new PorterDuffCanvasView(blendMode);

            Grid.SetRow(canvasView, row + 1);
            Grid.SetColumn(canvasView, col);
            grid.Children.Add(canvasView);
        }

        Content = grid;
    }
}

결과:

포터 더프 그리드

포터-더프 블렌드 모드의 적절한 작동에 투명성이 중요하다고 확신할 수 있습니다. 클래스에는 PorterDuffCanvasView 메서드에 대한 총 3개의 호출이 포함됩니다 Canvas.Clear . 모두 모든 픽셀을 투명하게 설정하는 매개 변수가 없는 메서드를 사용합니다.

canvas.Clear();

픽셀이 불투명 흰색으로 설정되도록 이러한 호출을 변경해 보세요.

canvas.Clear(SKColors.White);

이러한 변경 후에는 일부 혼합 모드가 작동하는 것처럼 보이지만 다른 모드는 작동하지 않습니다. 원본 비트맵의 배경을 흰색 SrcOver 으로 설정하면 원본 비트맵에 대상을 표시할 수 있는 투명한 픽셀이 없기 때문에 모드가 작동하지 않습니다. 대상 비트맵 또는 캔버스의 배경을 흰색 DstOver 으로 설정하면 대상에 투명한 픽셀이 없기 때문에 작동하지 않습니다.

포터-더프 그리드 페이지의 비트맵을 DrawRect 간단한 호출로 바꾸려는 유혹이 있을 수 있습니다. 대상 사각형에는 적용되지만 원본 사각형에는 작동하지 않습니다. 원본 사각형은 단색 영역 이상을 포함해야 합니다. 원본 사각형에는 대상의 색이 지정된 영역에 해당하는 투명 영역이 포함되어야 합니다. 그런 다음에만 이러한 혼합 모드가 작동합니다.

포터 더프와 매트 사용

Brick-Wall Compositing 페이지는 클래식 작성 작업의 예를 보여 줍니다. 그림을 제거해야 하는 배경이 있는 비트맵을 포함하여 여러 조각에서 어셈블해야 합니다. 문제가 있는 배경을 가진 SeatedMonkey.jpg 비트맵은 다음과 같습니다.

앉은 원숭이

작성을 준비하기 위해 해당 매트 가 만들어졌는데, 이 비트맵은 이미지를 표시하고 투명하게 표시하려는 검은색의 또 다른 비트맵입니다. 이 파일의 이름은 SeatedMonkeyMatte.png 샘플의 Media 폴더에 있는 리소스 중 하나입니다.

앉은 원숭이 매트

이것은 전문적으로 만든 매트가 아닙니다 . 최적으로 매트는 검은색 픽셀의 가장자리 주위에 부분적으로 투명한 픽셀을 포함해야 하며 이 매트는 포함하지 않습니다.

Brick-Wall Compositing 페이지의 XAML 파일은 최종 이미지 작성 프로세스를 안내하는 및 사용자를 인스턴스화 SKCanvasViewButton 합니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.BrickWallCompositingPage"
             Title="Brick-Wall Compositing">

    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Button Text="Show sitting monkey"
                HorizontalOptions="Center"
                Margin="0, 10"
                Clicked="OnButtonClicked" />

    </StackLayout>
</ContentPage>

코드 숨김 파일은 필요한 두 비트맵을 로드하고 해당 이벤트를 Button처리합니다Clicked. 클릭할 때마다 Button 필드가 step 증분되고 새 Text 속성이 에 대해 Button설정됩니다. 5에 도달하면 step 0으로 다시 설정됩니다.

public partial class BrickWallCompositingPage : ContentPage
{
    SKBitmap monkeyBitmap = BitmapExtensions.LoadBitmapResource(
        typeof(BrickWallCompositingPage),
        "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    SKBitmap matteBitmap = BitmapExtensions.LoadBitmapResource(
        typeof(BrickWallCompositingPage),
        "SkiaSharpFormsDemos.Media.SeatedMonkeyMatte.png");

    int step = 0;

    public BrickWallCompositingPage ()
    {
        InitializeComponent ();
    }

    void OnButtonClicked(object sender, EventArgs args)
    {
        Button btn = (Button)sender;
        step = (step + 1) % 5;

        switch (step)
        {
            case 0: btn.Text = "Show sitting monkey"; break;
            case 1: btn.Text = "Draw matte with DstIn"; break;
            case 2: btn.Text = "Draw sidewalk with DstOver"; break;
            case 3: btn.Text = "Draw brick wall with DstOver"; break;
            case 4: btn.Text = "Reset"; break;
        }

        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();
        ···
    }
}

프로그램이 처음 실행되면 다음 외에는 아무것도 표시되지 않습니다 Button.

벽돌 벽 작성 0단계

Button 한 번 step 누르면 1로 증가하면 PaintSurface 처리기가 SeatedMonkey.jpg 표시됩니다.

public partial class BrickWallCompositingPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        float x = (info.Width - monkeyBitmap.Width) / 2;
        float y = info.Height - monkeyBitmap.Height;

        // Draw monkey bitmap
        if (step >= 1)
        {
            canvas.DrawBitmap(monkeyBitmap, x, y);
        }
        ···
    }
}

개체가 없 SKPaint 으므로 혼합 모드가 없습니다. 비트맵은 화면 아래쪽에 나타납니다.

벽돌 벽 작성 1단계

다시 Button 누르고 step 2로 증분합니다. SeatedMonkeyMatte.png 파일을 표시하는 중요한 단계입니다.

public partial class BrickWallCompositingPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        // Draw matte to exclude monkey's surroundings
        if (step >= 2)
        {
            using (SKPaint paint = new SKPaint())
            {
                paint.BlendMode = SKBlendMode.DstIn;
                canvas.DrawBitmap(matteBitmap, x, y, paint);
            }
        }
        ···
    }
}

혼합 모드는 SKBlendMode.DstIn원본의 투명하지 않은 영역에 해당하는 영역에서 대상이 유지됨을 의미합니다. 원래 비트맵에 해당하는 대상 사각형의 re기본der가 투명해집니다.

벽돌 벽 작성 2단계

배경이 제거되었습니다.

다음 단계는 원숭이가 앉아있는 보도와 유사한 사각형을 그리는 것입니다. 이 보도의 모양은 단색 셰이더와 Perlin 노이즈 셰이더의 두 셰이더 구성을 기반으로 합니다.

public partial class BrickWallCompositingPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        const float sidewalkHeight = 80;
        SKRect rect = new SKRect(info.Rect.Left, info.Rect.Bottom - sidewalkHeight,
                                 info.Rect.Right, info.Rect.Bottom);

        // Draw gravel sidewalk for monkey to sit on
        if (step >= 3)
        {
            using (SKPaint paint = new SKPaint())
            {
                paint.Shader = SKShader.CreateCompose(
                                    SKShader.CreateColor(SKColors.SandyBrown),
                                    SKShader.CreatePerlinNoiseTurbulence(0.1f, 0.3f, 1, 9));

                paint.BlendMode = SKBlendMode.DstOver;
                canvas.DrawRect(rect, paint);
            }
        }
        ···
    }
}

이 보도는 원숭이 뒤에 가야하기 때문에 혼합 모드는 DstOver. 대상은 배경이 투명한 경우에만 표시됩니다.

벽돌 벽 작성 3단계

마지막 단계는 벽돌 벽을 추가하는 것입니다. 이 프로그램은 클래스에서 정적 속성 BrickWallTile 으로 사용할 수 있는 벽돌 벽 비트맵 타일을 AlgorithmicBrickWallPage 사용합니다. 아래쪽 행이 전체 타일이 되도록 타일을 이동하는 변환 변환이 호출에 추가 SKShader.CreateBitmap 됩니다.

public partial class BrickWallCompositingPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        // Draw bitmap tiled brick wall behind monkey
        if (step >= 4)
        {
            using (SKPaint paint = new SKPaint())
            {
                SKBitmap bitmap = AlgorithmicBrickWallPage.BrickWallTile;
                float yAdjust = (info.Height - sidewalkHeight) % bitmap.Height;

                paint.Shader = SKShader.CreateBitmap(bitmap,
                                                     SKShaderTileMode.Repeat,
                                                     SKShaderTileMode.Repeat,
                                                     SKMatrix.MakeTranslation(0, yAdjust));
                paint.BlendMode = SKBlendMode.DstOver;
                canvas.DrawRect(info.Rect, paint);
            }
        }
    }
}

편의를 위해 호출은 DrawRect 전체 캔버스에 이 셰이더를 표시하지만 모드는 DstOver 출력을 여전히 투명한 캔버스 영역으로만 제한합니다.

벽돌 벽 작성 4단계

분명히이 장면을 구성하는 다른 방법이 있습니다. 백그라운드에서 시작하여 포그라운드로 진행하여 빌드할 수 있습니다. 그러나 혼합 모드를 사용하면 더 많은 유연성을 얻을 수 있습니다. 특히 매트를 사용하면 비트맵의 배경을 구성된 장면에서 제외할 수 있습니다.

경로 및 지역을 사용한 클리핑 문서에서 배운 것처럼 클래스 SKCanvas 는 세 가지 유형의 클리핑을 정의하며, 이 형식은 해당 ClipRegionClipPath및 메서드를 ClipRect정의합니다. Porter-Duff 혼합 모드는 비트맵을 포함하여 이미지를 그릴 수 있는 모든 항목으로 제한할 수 있는 다른 유형의 클리핑을 추가합니다. 벽돌 벽 작성사용되는 매트는 기본적으로 클리핑 영역을 정의합니다.

그라데이션 투명도 및 전환

이 문서의 앞부분에 표시된 Porter-Duff 혼합 모드의 예에는 불투명 픽셀과 투명한 픽셀로 구성된 모든 관련 이미지가 있지만 부분적으로 투명한 픽셀은 아닙니다. 혼합 모드 함수도 해당 픽셀에 대해 정의됩니다. 다음 표는 Skia SkBlendMode 참조에 있는 표기법을 사용하는 Porter-Duff 혼합 모드에 대한 보다 공식적인 정의입니다. (왜냐하면 )SkBlendMode 참조 는 Skia 참조이며 C++ 구문이 사용됩니다.)

개념적으로 각 픽셀의 빨강, 녹색, 파랑 및 알파 구성 요소는 바이트에서 0에서 1 사이의 부동 소수점 숫자로 변환됩니다. 알파 채널의 경우 0은 완전히 투명하고 1은 완전히 불투명합니다.

아래 표의 표기법은 다음 약어를 사용합니다.

  • Da 는 대상 알파 채널입니다.
  • Dc 는 대상 RGB 색입니다.
  • Sa 는 원본 알파 채널입니다.
  • Sc 는 원본 RGB 색입니다.

RGB 색은 알파 값을 미리 곱합니다. 예를 들어 Sc가 순수 빨간색을 나타내지만 Sa가 0x80 경우 RGB 색은 (0x80, 0, 0)입니다. Sa가 0이면 모든 RGB 구성 요소도 0입니다.

결과는 알파 채널과 RGB 색이 쉼표 로 구분된 대괄호로 표시됩니다. [alpha, color]. 색의 경우 빨강, 녹색 및 파랑 구성 요소에 대해 계산이 별도로 수행됩니다.

모드 연산
Clear [0, 0]
Src [Sa, Sc]
Dst [Da, Dc]
SrcOver [Sa + Da· (1 – Sa), Sc + Dc· (1 – Sa)
DstOver [Da + Sa· (1 – Da), Dc + Sc· (1 – Da)
SrcIn [Sa· Da, Sc· Da]
DstIn [Da· Sa, Dc·Sa]
SrcOut [Sa· (1 – Da), Sc· (1 – Da)]
DstOut [Da· (1 – Sa), Dc· (1 – Sa)]
SrcATop [Da, Sc· Da + Dc· (1 – Sa)]
DstATop [Sa, Dc·Sa + Sc· (1 – Da)]
Xor [Sa + Da – 2· Sa· Da, Sc· (1 – Da) + Dc· (1 – Sa)]
Plus [Sa + Da, Sc + Dc]
Modulate [Sa· Da, Sc· Dc]

이러한 작업은 DaSa가 0 또는 1일 때 더 쉽게 분석할 수 있습니다. 예를 들어 기본 SrcOver 모드의 경우 Sa가 0이면 Sc도 0이고 결과는 대상 알파 및 색인 [Da, Dc]입니다. Sa가 1이면 결과는 [Sa, Sc], 원본 알파 및 색 또는 [1, Sc]입니다.

Plus 및 모드는 원본과 Modulate 대상의 조합으로 인해 새 색이 발생할 수 있다는 다른 색과 약간 다릅니다. Plus 바이트 구성 요소 또는 부동 소수점 구성 요소를 사용하여 모드를 해석할 수 있습니다. 앞서 표시된 Porter-Duff Grid 페이지에서 대상 색은(0xC0, 0x80, 0x00) 원본 색(0x00, 0x80, 0xC0)입니다. 각 구성 요소 쌍이 추가되지만 합계는 0xFF 고정됩니다. 결과는 색 (0xC0, 0xFF, 0xC0)입니다. 교집합에 표시되는 색입니다.

모드의 Modulate 경우 RGB 값을 부동 소수점으로 변환해야 합니다. 대상 색은 (0.75, 0.5, 0)이고 원본은 (0, 0.5, 0.75)입니다. RGB 구성 요소는 각각 함께 곱하고 결과는 (0, 0.25, 0)입니다. 이 모드의 포터-더프 그리드 페이지의 교집합에 표시되는 색입니다.

포터-더프 투명도 페이지에서는 포터-더프 혼합 모드가 부분적으로 투명한 그래픽 개체에서 작동하는 방식을 검사할 수 있습니다. XAML 파일에는 Porter-Duff 모드가 포함되어 Picker 있습니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
             xmlns:skiaviews="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.PorterDuffTransparencyPage"
             Title="Porter-Duff Transparency">

    <StackLayout>
        <skiaviews:SKCanvasView x:Name="canvasView"
                                VerticalOptions="FillAndExpand"
                                PaintSurface="OnCanvasViewPaintSurface" />

        <Picker x:Name="blendModePicker"
                Title="Blend Mode"
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKBlendMode}">
                    <x:Static Member="skia:SKBlendMode.Clear" />
                    <x:Static Member="skia:SKBlendMode.Src" />
                    <x:Static Member="skia:SKBlendMode.Dst" />
                    <x:Static Member="skia:SKBlendMode.SrcOver" />
                    <x:Static Member="skia:SKBlendMode.DstOver" />
                    <x:Static Member="skia:SKBlendMode.SrcIn" />
                    <x:Static Member="skia:SKBlendMode.DstIn" />
                    <x:Static Member="skia:SKBlendMode.SrcOut" />
                    <x:Static Member="skia:SKBlendMode.DstOut" />
                    <x:Static Member="skia:SKBlendMode.SrcATop" />
                    <x:Static Member="skia:SKBlendMode.DstATop" />
                    <x:Static Member="skia:SKBlendMode.Xor" />
                    <x:Static Member="skia:SKBlendMode.Plus" />
                    <x:Static Member="skia:SKBlendMode.Modulate" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                3
            </Picker.SelectedIndex>
        </Picker>
    </StackLayout>
</ContentPage>

코드 숨김 파일은 선형 그라데이션을 사용하여 동일한 크기의 두 사각형을 채웁니다. 대상 그라데이션은 오른쪽 위에서 왼쪽 아래까지입니다. 오른쪽 위 모서리에서는 갈색이지만 중앙을 향해 투명하게 퇴색하기 시작하고 왼쪽 아래 모서리에 투명합니다.

원본 사각형에는 왼쪽 위에서 오른쪽 아래까지 그라데이션이 있습니다. 왼쪽 위 모서리는 흐리지만 다시 투명하게 페이드되어 오른쪽 아래 모서리에 투명합니다.

public partial class PorterDuffTransparencyPage : ContentPage
{
    public PorterDuffTransparencyPage()
    {
        InitializeComponent();
    }

    void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Make square display rectangle smaller than canvas
        float size = 0.9f * Math.Min(info.Width, info.Height);
        float x = (info.Width - size) / 2;
        float y = (info.Height - size) / 2;
        SKRect rect = new SKRect(x, y, x + size, y + size);

        using (SKPaint paint = new SKPaint())
        {
            // Draw destination
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(rect.Right, rect.Top),
                                new SKPoint(rect.Left, rect.Bottom),
                                new SKColor[] { new SKColor(0xC0, 0x80, 0x00),
                                                new SKColor(0xC0, 0x80, 0x00, 0) },
                                new float[] { 0.4f, 0.6f },
                                SKShaderTileMode.Clamp);

            canvas.DrawRect(rect, paint);

            // Draw source
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(rect.Left, rect.Top),
                                new SKPoint(rect.Right, rect.Bottom),
                                new SKColor[] { new SKColor(0x00, 0x80, 0xC0),
                                                new SKColor(0x00, 0x80, 0xC0, 0) },
                                new float[] { 0.4f, 0.6f },
                                SKShaderTileMode.Clamp);

            // Get the blend mode from the picker
            paint.BlendMode = blendModePicker.SelectedIndex == -1 ? 0 :
                                    (SKBlendMode)blendModePicker.SelectedItem;

            canvas.DrawRect(rect, paint);

            // Stroke surrounding rectangle
            paint.Shader = null;
            paint.BlendMode = SKBlendMode.SrcOver;
            paint.Style = SKPaintStyle.Stroke;
            paint.Color = SKColors.Black;
            paint.StrokeWidth = 3;
            canvas.DrawRect(rect, paint);
        }
    }
}

이 프로그램은 포터-더프 혼합 모드를 비트맵 이외의 그래픽 개체와 함께 사용할 수 있음을 보여 줍니다. 그러나 원본에는 투명 영역이 포함되어야 합니다. 그라데이션이 사각형을 채우지만 그라데이션의 일부가 투명하기 때문입니다.

다음은 세 가지 예입니다.

포터-더프 투명도

대상 및 원본의 구성은 원래 Porter-Duff Compositing Digital Images 용지의 255페이지에 표시된 다이어그램과 매우 유사하지만, 이 페이지는 혼합 모드가 부분 투명도 영역에 대해 잘 동작한다는 것을 보여줍니다.

몇 가지 다른 효과에 투명 그라데이션을 사용할 수 있습니다. 한 가지 가능성은 SkiaSharp 원형 그라데이션 페이지의 마스킹 섹션에 대한 방사형 그라데이션에 표시된 기술과 유사한 마스킹입니다. 작성 마스크 페이지의 대부분은 이전 프로그램과 비슷합니다. 비트맵 리소스를 로드하고 표시할 사각형을 결정합니다. 방사형 그라데이션은 미리 결정된 가운데 및 반지름을 기반으로 생성됩니다.

public class CompositingMaskPage : ContentPage
{
    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
        typeof(CompositingMaskPage),
        "SkiaSharpFormsDemos.Media.MountainClimbers.jpg");

    static readonly SKPoint CENTER = new SKPoint(180, 300);
    static readonly float RADIUS = 120;

    public CompositingMaskPage ()
    {
        Title = "Compositing Mask";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Find rectangle to display bitmap
        float scale = Math.Min((float)info.Width / bitmap.Width,
                               (float)info.Height / bitmap.Height);

        SKRect rect = SKRect.Create(scale * bitmap.Width, scale * bitmap.Height);

        float x = (info.Width - rect.Width) / 2;
        float y = (info.Height - rect.Height) / 2;
        rect.Offset(x, y);

        // Display bitmap in rectangle
        canvas.DrawBitmap(bitmap, rect);

        // Adjust center and radius for scaled and offset bitmap
        SKPoint center = new SKPoint(scale * CENTER.X + x,
                                        scale * CENTER.Y + y);
        float radius = scale * RADIUS;

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateRadialGradient(
                                center,
                                radius,
                                new SKColor[] { SKColors.Black,
                                                SKColors.Transparent },
                                new float[] { 0.6f, 1 },
                                SKShaderTileMode.Clamp);

            paint.BlendMode = SKBlendMode.DstIn;

            // Display rectangle using that gradient and blend mode
            canvas.DrawRect(rect, paint);
        }

        canvas.DrawColor(SKColors.Pink, SKBlendMode.DstOver);
    }
}

이 프로그램의 차이점은 그라데이션이 가운데에 검은색으로 시작하여 투명도로 끝난다는 것입니다. 혼합 모드가 있는 비트맵에 표시됩니다. 이 모드 DstIn는 투명하지 않은 원본 영역에서만 대상을 표시합니다.

호출 후에 DrawRect 는 방사형 그라데이션으로 정의된 원을 제외하고 캔버스의 전체 표면이 투명합니다. 마지막 호출이 수행됩니다.

canvas.DrawColor(SKColors.Pink, SKBlendMode.DstOver);

캔버스의 모든 투명 영역은 분홍색으로 칠해집니다.

마스크 작성

또한 한 이미지에서 다른 이미지로 전환하기 위해 Porter-Duff 모드 및 부분적으로 투명한 그라데이션을 사용할 수 있습니다. 그라데이션 전환 페이지에는 0에서 1로의 전환 진행률 수준을 나타내고 Picker 원하는 전환 유형을 선택하는 것이 포함 Slider 됩니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.GradientTransitionsPage"
             Title="Gradient Transitions">

    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="progressSlider"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference progressSlider},
                              Path=Value,
                              StringFormat='Progress = {0:F2}'}"
               HorizontalTextAlignment="Center" />

        <Picker x:Name="transitionPicker"
                Title="Transition"
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged" />

    </StackLayout>
</ContentPage>

코드 숨김 파일은 전환을 보여 주는 두 개의 비트맵 리소스를 로드합니다. 이 문서의 앞부분에서 비트맵 디졸브 페이지에 사용된 동일한 두 이미지입니다. 또한 이 코드는 세 가지 유형의 그라데이션(선형, 방사형 및 스윕)에 해당하는 세 개의 멤버를 사용하여 열거형을 정의합니다. 이러한 값은 다음으로 Picker로드됩니다.

public partial class GradientTransitionsPage : ContentPage
{
    SKBitmap bitmap1 = BitmapExtensions.LoadBitmapResource(
        typeof(GradientTransitionsPage),
        "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    SKBitmap bitmap2 = BitmapExtensions.LoadBitmapResource(
        typeof(GradientTransitionsPage),
        "SkiaSharpFormsDemos.Media.FacePalm.jpg");

    enum TransitionMode
    {
        Linear,
        Radial,
        Sweep
    };

    public GradientTransitionsPage ()
    {
        InitializeComponent ();

        foreach (TransitionMode mode in Enum.GetValues(typeof(TransitionMode)))
        {
            transitionPicker.Items.Add(mode.ToString());
        }

        transitionPicker.SelectedIndex = 0;
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }
    ···
}

코드 숨김 파일은 세 개의 SKPaint 개체를 만듭니다. 개체는 paint0 혼합 모드를 사용하지 않습니다. 이 페인트 개체는 배열에 표시된 대로 검은색에서 투명으로 가는 그라데이션이 있는 사각형을 colors 그리는 데 사용됩니다. 배열은 positions 위치 Slider()를 기반으로 하지만 다소 조정됩니다. Slider 최소 또는 최대 progress 값인 경우 값은 0 또는 1이고 두 비트맵 중 하나가 완전히 표시되어야 합니다. 해당 값에 positions 맞게 배열을 설정해야 합니다.

값이 progress 0이면 배열에 positions -0.1 및 0 값이 포함됩니다. SkiaSharp는 첫 번째 값을 0과 같게 조정합니다. 즉, 그라데이션은 0에서만 검은색이고 그렇지 않으면 투명합니다. 0.5이면 progress 배열에 0.45 및 0.55 값이 포함됩니다. 그라데이션은 0에서 0.45로 검정으로 전환된 다음 투명으로 전환되며 0.55에서 1로 완전히 투명합니다. 1 positions 인 경우 progress 배열은 1과 1.1이며, 이는 그라데이션이 0에서 1까지 검은색임을 의미합니다.

position 배열은 colors 그라데이션을 만드는 세 가지 메서드 SKShader 에 모두 사용됩니다. 선택 영역에 따라 Picker 다음 셰이더 중 하나만 생성됩니다.

public partial class GradientTransitionsPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Assume both bitmaps are square for display rectangle
        float size = Math.Min(info.Width, info.Height);
        SKRect rect = SKRect.Create(size, size);
        float x = (info.Width - size) / 2;
        float y = (info.Height - size) / 2;
        rect.Offset(x, y);

        using (SKPaint paint0 = new SKPaint())
        using (SKPaint paint1 = new SKPaint())
        using (SKPaint paint2 = new SKPaint())
        {
            SKColor[] colors = new SKColor[] { SKColors.Black,
                                               SKColors.Transparent };

            float progress = (float)progressSlider.Value;

            float[] positions = new float[]{ 1.1f * progress - 0.1f,
                                             1.1f * progress };

            switch ((TransitionMode)transitionPicker.SelectedIndex)
            {
                case TransitionMode.Linear:
                    paint0.Shader = SKShader.CreateLinearGradient(
                                        new SKPoint(rect.Left, 0),
                                        new SKPoint(rect.Right, 0),
                                        colors,
                                        positions,
                                        SKShaderTileMode.Clamp);
                    break;

                case TransitionMode.Radial:
                    paint0.Shader = SKShader.CreateRadialGradient(
                                        new SKPoint(rect.MidX, rect.MidY),
                                        (float)Math.Sqrt(Math.Pow(rect.Width / 2, 2) +
                                                         Math.Pow(rect.Height / 2, 2)),
                                        colors,
                                        positions,
                                        SKShaderTileMode.Clamp);
                    break;

                case TransitionMode.Sweep:
                    paint0.Shader = SKShader.CreateSweepGradient(
                                        new SKPoint(rect.MidX, rect.MidY),
                                        colors,
                                        positions);
                    break;
            }

            canvas.DrawRect(rect, paint0);

            paint1.BlendMode = SKBlendMode.SrcOut;
            canvas.DrawBitmap(bitmap1, rect, paint1);

            paint2.BlendMode = SKBlendMode.DstOver;
            canvas.DrawBitmap(bitmap2, rect, paint2);
        }
    }
}

해당 그라데이션은 혼합 모드 없이 사각형에 표시됩니다. 해당 DrawRect 호출 후에 캔버스는 검은색에서 투명으로 그라데이션을 포함합니다. 검정 크기가 더 높은 Slider 값으로 증가합니다.

처리기의 마지막 네 문 PaintSurface 에서 두 비트맵이 표시됩니다. 혼합 모드는 SrcOut 첫 번째 비트맵이 배경의 투명한 영역에만 표시됨을 의미합니다. 두 번째 비트맵 모드는 DstOver 첫 번째 비트맵이 표시되지 않는 영역에만 두 번째 비트맵이 표시됨을 의미합니다.

다음 스크린샷은 각각 50% 표시에 있는 세 가지 전환 유형을 보여 줍니다.

그라데이션 전환