Фильтры изображений SkiaSharp
Фильтры изображений — это эффекты, которые работают со всеми битами цветов пикселей, составляющих изображение. Они являются более универсальными, чем фильтры маски, которые работают только на альфа-канале, как описано в статье Фильтры маски SkiaSharp. Чтобы использовать фильтр изображений, задайте ImageFilter
свойство SKPaint
объекта типа SKImageFilter
, созданного путем вызова одного из статических методов класса.
Лучший способ ознакомиться с фильтрами маски — экспериментировать с этими статическими методами. Фильтр маски можно использовать для размытия всего растрового изображения:
В этой статье также показано использование фильтра изображений для создания тени капли, а также для эффектов гравюры и гравюры.
Размытие векторной графики и растровых изображений
Эффект размытия, созданный 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.CreateBlur
объекта, используемого SKPaint
для отображения текста и растрового изображения:
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);
}
}
}
На трех снимках экрана показаны различные параметры sigmaX
и sigmaY
параметры:
Чтобы обеспечить согласованность размытия между различными размерами и разрешениями дисплея, задайте sigmaX
и sigmaY
значения, пропорциональные размеру отрисованного пикселя изображения, к которому применяется размытие.
Тень
Статический 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);
Присвойте этому объекту свойство SKPaint
объектаImageFilter
, и все, что вы рисуете с этим объектом, будет иметь за ним тень падения.
dy
Параметры dx
указывают горизонтальные и вертикальные смещения тени в пикселях из графического объекта. Соглашение в 2D-графике заключается в том, чтобы предположить источник света, поступающий из верхнего левого угла, что означает, что оба этих аргумента должны быть положительными для размещения тени ниже и справа от графического объекта.
sigmaY
Параметры sigmaX
— это размытые факторы для тени падения.
Параметр color
— это цвет тени падения. Это SKColor
значение может включать прозрачность. Одним из возможных вариантов является значение SKColors.Black.WithAlpha(0x80)
цвета для темного фона любого цвета.
Последние два параметра являются необязательными.
Программа Drop Shadow Experiment позволяет экспериментировать со значениями dx
, dy
sigmaX
а также sigmaY
отображать текстовую строку с тенью. Файл XAML создает экземпляры четырех 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
определяет шесть методов, которые имеют аналогичные имена и параметры, перечисленные здесь в порядке повышения сложности:
CreateDistantLitDiffuse
CreateDistantLitSpecular
CreatePointLitDiffuse
CreatePointLitSpecular
CreateSpotLitDiffuse
CreateSpotLitSpecular
Эти методы создают фильтры изображений, которые имитируют эффект различных видов света на трехмерных поверхностях. Результирующий фильтр изображений освещает двухмерные объекты, как если бы они существовали в трехмерном пространстве, что может привести к отображению этих объектов с повышенными или отступами или с зрительным выделением.
Методы Distant
света предполагают, что свет исходит от далекого расстояния. Для освещения объектов предполагается, что свет указывает на одно согласованное направление в трехмерном пространстве, как Солнце на небольшой площади Земли. Методы Point
света имитируют лампочку, расположенную в трехмерном пространстве, который выдает свет во всех направлениях. Свет Spot
имеет как позицию, так и направление, как фонарик.
Расположения и направления в трехмерном пространстве указываются со значениями SKPoint3
структуры, которая похожа на SKPoint
три свойства с именем X
, Y
и Z
.
Количество и сложность параметров для этих методов затрудняют экспериментирование. Чтобы приступить к работе, страница "Удаленный световый эксперимент" позволяет экспериментировать с параметрами методаCreateDistantLightDiffuse
:
public static SKImageFilter CreateDistantLitDiffuse (SKPoint3 direction,
SKColor lightColor,
float surfaceScale,
float kd,
SKImageFilter input = null,
SKImageFilter.CropRect cropRect = null);
Страница не использует последние два необязательных параметра.
Три Slider
представления в XAML-файле позволяют выбрать Z
координату SKPoint3
значения, surfaceScale
параметра и kd
параметра, определенного в документации ПО API в качестве "константы диффузного освещения".
<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
должно быть положительным. (Программа позволяет отрицательным значениям, чтобы увидеть, что они вызывают эффект для остановки работы.) Более высокие значения вызывают более интенсивный свет.
Эти факторы можно сбалансировать для получения эффектов, когда отрицательный (как и с экранами с iOS и Android) и выгравированный эффект surfaceScale
, когда surfaceScale
положительный, как и с снимок экрана UWP справа:
Снимок экрана Android имеет значение Z 0, что означает, что свет указывает только вниз и справа. Фон не освещается, а поверхность текстовой строки не освещается. Свет влияет только на край текста для очень тонкого эффекта.
Альтернативный подход к выгравированному и выгравированному тексту был показан в статье Преобразование перевода: текстовая строка отображается дважды с различными цветами, которые немного смещаются друг от друга.