SkiaSharp 이미지 필터

이미지 필터는 이미지를 구성하는 픽셀의 모든 색 비트에서 작동하는 효과입니다. SkiaSharp 마스크 필터 문서에 설명된 대로 알파 채널에서만 작동하는 마스크 필터보다 더 다양합니다. 이미지 필터를 사용하려면 클래스의 SKPaint 정적 메서드 중 하나를 호출하여 만든 형식 SKImageFilter 의 개체로 속성을 설정합니다ImageFilter.

마스크 필터에 익숙해지는 가장 좋은 방법은 이러한 정적 메서드를 실험하는 것입니다. 마스크 필터를 사용하여 전체 비트맵을 흐리게 표시할 수 있습니다.

흐림 예제

또한 이 문서에서는 이미지 필터를 사용하여 그림자를 만들고 효과를 구현하고 조각하는 방법을 보여 줍니다.

벡터 그래픽 및 비트맵 흐리게 표시

정적 메서드에서 SKImageFilter.CreateBlur 만든 흐림 효과 클래스의 흐림 메서드에 SKMaskFilter 비해 상당한 이점이 있습니다. 이미지 필터는 전체 비트맵을 흐리게 할 수 있습니다. 메서드의 구문은 다음과 같습니다.

public static SkiaSharp.SKImageFilter CreateBlur (float sigmaX, float sigmaY,
                                                  SKImageFilter input = null,
                                                  SKImageFilter.CropRect cropRect = null);

메서드에는 가로 방향의 흐림 익스텐트의 첫 번째 값과 세로 방향에 대한 두 번째 시그마 값이 있습니다. 다른 이미지 필터를 선택적 세 번째 인수로 지정하여 이미지 필터를 연계할 수 있습니다. 자르기 사각형을 지정할 수도 있습니다.

샘플의 이미지 흐림 실험 페이지에는 다양한 수준의 흐림 효과를 설정하는 실험을 수행할 수 있는 두 가지 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.ImageBlurExperimentPage"
             Title="Image Blur Experiment">

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

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

        <Label Text="{Binding Source={x:Reference sigmaXSlider},
                              Path=Value,
                              StringFormat='Sigma X = {0:F1}'}"
               HorizontalTextAlignment="Center" />

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

        <Label Text="{Binding Source={x:Reference sigmaYSlider},
                              Path=Value,
                              StringFormat='Sigma Y = {0:F1}'}"
               HorizontalTextAlignment="Center" />
    </StackLayout>
</ContentPage>

코드 숨김 파일은 두 Slider 값을 사용하여 텍스트와 비트맵을 모두 표시하는 데 사용되는 개체를 호출 SKImageFilter.CreateBlurSKPaint 합니다.

public partial class ImageBlurExperimentPage : ContentPage
{
    const string TEXT = "Blur My Text";

    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
                            typeof(MaskBlurExperimentPage),
                            "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");

    public ImageBlurExperimentPage ()
    {
        InitializeComponent ();
    }

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

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

        canvas.Clear(SKColors.Pink);

        // Get values from sliders
        float sigmaX = (float)sigmaXSlider.Value;
        float sigmaY = (float)sigmaYSlider.Value;

        using (SKPaint paint = new SKPaint())
        {
            // Set SKPaint properties
            paint.TextSize = (info.Width - 100) / (TEXT.Length / 2);
            paint.ImageFilter = SKImageFilter.CreateBlur(sigmaX, sigmaY);

            // Get text bounds and calculate display rectangle
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);
            SKRect textRect = new SKRect(0, 0, info.Width, textBounds.Height + 50);

            // Center the text in the display rectangle
            float xText = textRect.Width / 2 - textBounds.MidX;
            float yText = textRect.Height / 2 - textBounds.MidY;

            canvas.DrawText(TEXT, xText, yText, paint);

            // Calculate rectangle for bitmap
            SKRect bitmapRect = new SKRect(0, textRect.Bottom, info.Width, info.Height);
            bitmapRect.Inflate(-50, -50);

            canvas.DrawBitmap(bitmap, bitmapRect, BitmapStretch.Uniform, paint: paint);
        }
    }
}

세 개의 스크린샷은 설정 및 sigmaY 설정에 sigmaX 대한 다양한 설정을 보여 줍니다.

이미지 흐림 실험

다양한 표시 크기 및 해상도 간에 흐림 효과를 일관되게 유지하려면 흐림 효과가 적용되는 이미지의 렌더링된 픽셀 크기에 비례하는 값을 설정하고 sigmaXsigmaY 설정합니다.

그림자 처리

정적 메서드는 SKImageFilter.CreateDropShadow 그림자에 SKImageFilter 대한 개체를 만듭니다.

public static SKImageFilter CreateDropShadow (float dx, float dy,
                                              float sigmaX, float sigmaY,
                                              SKColor color,
                                              SKDropShadowImageFilterShadowMode shadowMode,
                                              SKImageFilter input = null,
                                              SKImageFilter.CropRect cropRect = null);

이 개체를 ImageFilter 개체의 SKPaint 속성으로 설정하면 해당 개체를 사용하여 그리는 모든 개체 뒤에 그림자가 있습니다.

dy 매개 변수는 dx 그래픽 개체에서 픽셀 단위로 그림자의 가로 및 세로 오프셋을 나타냅니다. 2D 그래픽의 규칙은 왼쪽 위에서 오는 광원을 가정하는 것입니다. 이는 이러한 인수가 모두 그래픽 개체의 아래와 오른쪽에 그림자를 배치하는 것이 양수여야 임을 의미합니다.

sigmaXsigmaY 매개 변수는 그림자에 대한 흐리게 요소입니다.

color 매개 변수는 그림자의 색입니다. 이 SKColor 값은 투명도를 포함할 수 있습니다. 한 가지 가능성은 색 배경을 어둡게 하는 색 값 SKColors.Black.WithAlpha(0x80) 입니다.

마지막 두 매개 변수는 선택 사항입니다.

그림자 실험 프로그램을 사용하면 < a0sigmaXdy/> dx값을 실험하고 sigmaY 그림자가 있는 텍스트 문자열을 표시할 수 있습니다. XAML 파일은 다음 값을 설정하기 위해 4개의 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.DropShadowExperimentPage"
             Title="Drop Shadow Experiment">
    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="Margin" Value="10, 0" />
        </Style>

        <Style TargetType="Label">
            <Setter Property="HorizontalTextAlignment" Value="Center" />
        </Style>
    </ContentPage.Resources>

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

        <Slider x:Name="dxSlider"
                Minimum="-20"
                Maximum="20"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference dxSlider},
                              Path=Value,
                              StringFormat='Horizontal offset = {0:F1}'}" />

        <Slider x:Name="dySlider"
                Minimum="-20"
                Maximum="20"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference dySlider},
                              Path=Value,
                              StringFormat='Vertical offset = {0:F1}'}" />

        <Slider x:Name="sigmaXSlider"
                Maximum="10"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference sigmaXSlider},
                              Path=Value,
                              StringFormat='Sigma X = {0:F1}'}" />

        <Slider x:Name="sigmaYSlider"
                Maximum="10"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference sigmaYSlider},
                              Path=Value,
                              StringFormat='Sigma Y = {0:F1}'}" />
    </StackLayout>
</ContentPage>

코드 숨김 파일은 이러한 값을 사용하여 파란색 텍스트 문자열에 빨간색 그림자를 만듭니다.

public partial class DropShadowExperimentPage : ContentPage
{
    const string TEXT = "Drop Shadow";

    public DropShadowExperimentPage ()
    {
        InitializeComponent ();
    }

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

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

        canvas.Clear();

        // Get values from sliders
        float dx = (float)dxSlider.Value;
        float dy = (float)dySlider.Value;
        float sigmaX = (float)sigmaXSlider.Value;
        float sigmaY = (float)sigmaYSlider.Value;

        using (SKPaint paint = new SKPaint())
        {
            // Set SKPaint properties
            paint.TextSize = info.Width / 7;
            paint.Color = SKColors.Blue;
            paint.ImageFilter = SKImageFilter.CreateDropShadow(
                                    dx,
                                    dy,
                                    sigmaX,
                                    sigmaY,
                                    SKColors.Red,
                                    SKDropShadowImageFilterShadowMode.DrawShadowAndForeground);

            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            // Center the text in the display rectangle
            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2 - textBounds.MidY;

            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

실행 중인 프로그램은 다음과 같습니다.

그림자 실험

맨 오른쪽에 있는 유니버설 Windows 플랫폼 스크린샷의 음수 오프셋 값으로 인해 그림자가 텍스트의 위와 왼쪽에 표시됩니다. 이는 컴퓨터 그래픽에 대한 규칙이 아닌 오른쪽 아래의 광원을 제안합니다. 그러나 그림자도 매우 흐릿하게 만들어지고 대부분의 그림자보다 더 장식적인 것처럼 보이기 때문에 어떤 식으로든 잘못되지 않는 것처럼 보입니다.

조명 효과

클래스는 SKImageFilter 복잡성을 높이기 위해 여기에 나열된 이름과 매개 변수가 비슷한 6개의 메서드를 정의합니다.

이러한 메서드는 3차원 표면에 다양한 종류의 빛의 효과를 모방하는 이미지 필터를 만듭니다. 결과 이미지 필터는 3D 공간에 있는 것처럼 2차원 개체를 비추므로 이러한 개체가 상승 또는 오목하거나 반사 강조 표시로 표시될 수 있습니다.

광원 메서드는 Distant 빛이 먼 거리에서 온다고 가정합니다. 물체를 조명하기 위해 빛은 지구의 작은 영역에 있는 태양과 마찬가지로 3D 공간에서 일관된 한 방향을 가리키는 것으로 간주됩니다. Point 광원 방법은 모든 방향으로 빛을 방출하는 3D 공간에 배치 된 전구를 모방합니다. 빛은 Spot 손전등처럼 위치와 방향을 모두 가지고 있습니다.

3D 공간의 위치와 방향은 모두 구조체의 SKPoint3 값으로 지정되며, 구조체 값은 비슷하 SKPoint 지만 이름이 3개인 XY속성과 함께 지정됩니다Z.

이러한 메서드에 대한 매개 변수의 수와 복잡성으로 인해 실험을 어렵게 만듭니다. 시작하려면 먼 조명 실험 페이지에서 메서드에 대한 매개 변수를 실험할 CreateDistantLightDiffuse 수 있습니다.

public static SKImageFilter CreateDistantLitDiffuse (SKPoint3 direction,
                                                     SKColor lightColor,
                                                     float surfaceScale,
                                                     float kd,
                                                     SKImageFilter input = null,
                                                     SKImageFilter.CropRect cropRect = null);

페이지는 마지막 두 개의 선택적 매개 변수를 사용하지 않습니다.

XAML 파일의 SKPoint3Slider 가지 뷰를 사용하면 API 설명서에 "확산 조명 상수"로 정의된 값surfaceScale, 매개 변수 및 kd 매개 변수의 좌표를 선택할 Z 수 있습니다.

<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="SkiaLightExperiment.MainPage"
             Title="Distant Light Experiment">

    <StackLayout>

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

        <Slider x:Name="zSlider"
                Minimum="-10"
                Maximum="10"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference zSlider},
                              Path=Value,
                              StringFormat='Z = {0:F0}'}"
               HorizontalTextAlignment="Center" />

        <Slider x:Name="surfaceScaleSlider"
                Minimum="-1"
                Maximum="1"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference surfaceScaleSlider},
                              Path=Value,
                              StringFormat='Surface Scale = {0:F1}'}"
               HorizontalTextAlignment="Center" />

        <Slider x:Name="lightConstantSlider"
                Minimum="-1"
                Maximum="1"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="{Binding Source={x:Reference lightConstantSlider},
                              Path=Value,
                              StringFormat='Light Constant = {0:F1}'}"
               HorizontalTextAlignment="Center" />
    </StackLayout>
</ContentPage>

코드 숨김 파일은 이러한 세 값을 가져오고 이를 사용하여 이미지 필터를 만들어 텍스트 문자열을 표시합니다.

public partial class DistantLightExperimentPage : ContentPage
{
    const string TEXT = "Lighting";

    public DistantLightExperimentPage()
    {
        InitializeComponent();
    }

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

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

        canvas.Clear();

        float z = (float)zSlider.Value;
        float surfaceScale = (float)surfaceScaleSlider.Value;
        float lightConstant = (float)lightConstantSlider.Value;

        using (SKPaint paint = new SKPaint())
        {
            paint.IsAntialias = true;

            // Size text to 90% of canvas width
            paint.TextSize = 100;
            float textWidth = paint.MeasureText(TEXT);
            paint.TextSize *= 0.9f * info.Width / textWidth;

            // Find coordinates to center text
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            float xText = info.Rect.MidX - textBounds.MidX;
            float yText = info.Rect.MidY - textBounds.MidY;

            // Create distant light image filter
            paint.ImageFilter = SKImageFilter.CreateDistantLitDiffuse(
                                    new SKPoint3(2, 3, z),
                                    SKColors.White,
                                    surfaceScale,
                                    lightConstant);

            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

첫 번째 인수 SKImageFilter.CreateDistantLitDiffuse 는 빛의 방향입니다. 양수 X 및 Y 좌표는 빛이 오른쪽과 아래쪽을 가리키고 있음을 나타냅니다. 양수 Z 좌표가 화면을 가리킵니다. XAML 파일을 사용하면 음의 Z 값을 선택할 수 있지만, 개념적으로 음의 Z 좌표로 인해 표시등이 화면을 가리킬 수 있습니다. 그 외의 작은 음수 값의 경우 조명 효과의 작동이 중지됩니다.

인수의 범위는 surfaceScale –1에서 1까지입니다. (더 높거나 낮은 값은 더 이상 영향을 주지 않습니다.) 다음은 캔버스 화면에서 그래픽 개체(이 경우 텍스트 문자열)의 변위를 나타내는 Z 축의 상대 값입니다. 음수 값을 사용하여 텍스트 문자열을 캔버스 표면 위로 올리고 양수 값을 사용하여 캔버스로 압축합니다.

값은 lightConstant 양수여야 합니다. (이 프로그램은 음수 값을 허용하므로 효과가 작동을 중지하는 것을 볼 수 있습니다.) 값이 높을수록 더 강렬한 빛이 발생합니다.

이러한 요인은 오른쪽의 UWP 스크린샷과 마찬가지로 음수일 때 surfaceScale (iOS 및 Android 스크린샷과 마찬가지로) 양수인 경우 surfaceScale 인그레이빙된 효과를 얻기 위해 균형을 맞출 수 있습니다.

먼 빛 실험

Android 스크린샷의 Z 값은 0입니다. 즉, 조명이 오른쪽을 가리키고 있습니다. 배경이 비추지 않고 텍스트 문자열의 표면도 비추지 않습니다. 조명은 텍스트의 가장자리에만 영향을 주어 매우 미묘한 효과를 줍니다.

변환 변환: 텍스트 문자열은 서로 약간 오프셋되는 서로 다른 색으로 두 번 표시됩니다.