Режимы смешения с разделителями

Как вы видели в статье SkiaSharp Porter-Duff blend режимы, режимы смешения Porter-Duff обычно выполняют операции вырезки. Режимы смешения с разделителями отличаются. Режимы сепарабельного изменения отдельных красных, зеленых и синих цветов изображения. Сепарируемые режимы смешивания могут смешивать цвет, чтобы продемонстрировать, что сочетание красного, зеленого и синего действительно белое:

Основные цвета

Светить и темнеть два способа

Обычно имеется растровое изображение, которое несколько слишком темно или слишком светло. Вы можете использовать режимы смешения с разделителями, чтобы осветить или темнеть изображение. Действительно, два режима сепарируемых смешения в SKBlendMode перечислении именуются Lighten и Darken.

Эти два режима демонстрируются на странице Lighten и Darken . XAML-файл создает экземпляры двух объектов и двух SKCanvasView 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.LightenAndDarkenPage"
             Title="Lighten and Darken">
    <StackLayout>
        <skia:SKCanvasView x:Name="lightenCanvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="lightenSlider"
                Margin="10"
                ValueChanged="OnSliderValueChanged" />

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

        <Slider x:Name="darkenSlider"
                Margin="10"
                ValueChanged="OnSliderValueChanged" />
    </StackLayout>
</ContentPage>

SKCanvasView Первая и демонстрацияSKBlendMode.Lighten, а Slider вторая пара демонстрируетSKBlendMode.Darken. Два Slider представления совместно используют один и тот же ValueChanged обработчик, и они SKCanvasView совместно используют один и тот же PaintSurface обработчик. Оба обработчика событий проверка, какой объект запускает событие:

public partial class LightenAndDarkenPage : ContentPage
{
    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
                typeof(SeparableBlendModesPage),
                "SkiaSharpFormsDemos.Media.Banana.jpg");

    public LightenAndDarkenPage ()
    {
        InitializeComponent ();
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        if ((Slider)sender == lightenSlider)
        {
            lightenCanvasView.InvalidateSurface();
        }
        else
        {
            darkenCanvasView.InvalidateSurface();
        }
    }

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

        canvas.Clear();

        // Find largest size rectangle in canvas
        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
        canvas.DrawBitmap(bitmap, rect);

        // Display gray rectangle with blend mode
        using (SKPaint paint = new SKPaint())
        {
            if ((SKCanvasView)sender == lightenCanvasView)
            {
                byte value = (byte)(255 * lightenSlider.Value);
                paint.Color = new SKColor(value, value, value);
                paint.BlendMode = SKBlendMode.Lighten;
            }
            else
            {
                byte value = (byte)(255 * (1 - darkenSlider.Value));
                paint.Color = new SKColor(value, value, value);
                paint.BlendMode = SKBlendMode.Darken;
            }

            canvas.DrawRect(rect, paint);
        }
    }
}

Обработчик PaintSurface вычисляет прямоугольник, подходящий для растрового изображения. Обработчик отображает это растровое изображение, а затем отображает прямоугольник по растровой карте, используя объект со своим BlendMode свойствомSKPaint, заданным SKBlendMode.Lighten или SKBlendMode.Darken. Свойство Color является серым оттенком на Sliderоснове . Lighten Для режима цветовые диапазоны от черного до белого, но для Darken режима он варьируется от белого до черного.

Снимок экрана слева направо показывает все Slider больше значений, так как верхний образ становится легче, а нижний рисунок становится темнее:

Свет и темный

Эта программа демонстрирует обычный способ использования режимов сепарабельного смешения: назначение — это изображение определенного рода, очень часто растровое изображение. Источник — это прямоугольник, отображаемый с помощью SKPaint объекта со своим BlendMode свойством, заданным в режиме сепарабельного смешения. Прямоугольник может быть сплошным цветом (как здесь) или градиентом. Прозрачность обычно не используется с режимами разбиения.

Поэкспериментируя с этой программой, вы обнаружите, что эти два режима смешивания не светятся и не темнеют изображение равномерно. Вместо этого, кажется, Slider установить порог определенного рода. Например, при увеличении Slider Lighten режима темные области изображения сначала получают свет, а более светлые области остаются прежними.

В режиме Lighten , если целевой пиксель является значением цвета RGB (dr, Dg, Db), а исходный пиксель — цветом (Sr, Sg, Sb), то выходные данные вычисляются следующим образом:

Or = max(Dr, Sr) Og = max(Dg, Sg) Ob = max(Db, Sb)

Для красного, зеленого и синего по отдельности результат — это больше места назначения и источника. Это создает эффект осветления темных областей назначения первым.

Режим Darken аналогичен, за исключением того, что результат меньше назначения и источника:

Or = min(Dr, Sr) Og = min(Dg, Sg) Ob = min(Db, Sb)

Красные, зеленые и синие компоненты обрабатываются отдельно, поэтому эти режимы смешивания называются режимами сепарабельной смеси. По этой причине аббревиаций Dc и Sc можно использовать для цветов назначения и источника, и понятно, что вычисления применяются к каждому из красных, зеленых и синих компонентов отдельно.

В следующей таблице показаны все режимы сепарируемых смешений с краткими объяснениями того, что они делают. Второй столбец показывает исходный цвет, который не приводит к изменению:

Режим смешивания Без изменений Операция
Plus Черный Светит путем добавления цветов: Sc + Dc
Modulate Белый Темнеет путем умножения цветов: Sc· Dc
Screen Черный Дополняет продукт дополнений: Sc + Dc – Sc · Dc
Overlay Серый Обратное HardLight
Darken Белый Минимум цветов: min(Sc, Dc)
Lighten Черный Максимальное количество цветов: max(Sc, Dc)
ColorDodge Черный Назначение Brightens на основе источника
ColorBurn Белый Темная цель на основе источника
HardLight Серый Похоже на эффект жесткого внимания
SoftLight Серый Аналогично эффекту мягкого внимания
Difference Черный Вычитает темнее из более светлого: Abs(Dc – Sc)
Exclusion Черный Difference Аналогично, но более низкая контрастность
Multiply Белый Темнеет путем умножения цветов: Sc· Dc

Более подробные алгоритмы можно найти в спецификации W3C Compositing и Blending Level 1 и в справочнике skia SkBlendMode, хотя нотация в этих двух источниках не совпадает. Помните, что Plus обычно считается режимом смешивания Porter-Duff и Modulate не является частью спецификации W3C.

Если источник прозрачный, то для всех режимов сепарируемых смешения, кроме Modulateтого, режим смешивания не действует. Как вы видели ранее, Modulate режим смешения включает альфа-канал в умножение. В противном случае имеет тот же эффект, Modulate что Multiplyи .

Обратите внимание на два режима с именем ColorDodge и ColorBurn. Слова уклоняются и горят в фотографических темных комнатах практики. Увеличение делает фотопечать, сияя светом через отрицательный. Без света печать белая. Печать становится темнее, так как больше света падает на печать в течение более длительного периода времени. Печатники часто использовали руку или небольшой объект, чтобы заблокировать некоторые из света от падения на определенную часть печати, что делает эту область светлее. Это называется dodging. И наоборот, непрозрачный материал с отверстием в нем (или руки блокируют большую часть света) можно использовать для направления большего света в определенном месте, чтобы темнеть его, называется сжиганием.

Программа Dodge и Burn очень похожа на Lighten и Darken. XAML-файл структурирован так же, но с различными именами элементов, и файл кода программной части также аналогичен, но эффект этих двух режимов смешения совершенно отличается:

Додж и Берн

Для небольших Slider значений Lighten режим сначала светит темные области, а ColorDodge светит более равномерно.

Программы приложений для обработки изображений часто позволяют выполнять отработку и сжигание в определенных областях, как и в темном помещении. Это можно сделать с помощью градиентов или растрового изображения с различными оттенками серого.

Изучение режимов сепарируемых смешения

Страница "Режимы смешивания с разделителями" позволяет изучить все режимы сепарабельного смешения. В нем отображается назначение растрового изображения и цветной прямоугольник с помощью одного из режимов смешения.

XAML-файл определяет Picker (для выбора режима смешивания) и четыре ползунка. Первые три ползунка позволяют задать красные, зеленые и синие компоненты источника. Четвертый ползунок предназначен для переопределения этих значений путем задания серого оттенка. Отдельные ползунки не определены, но цвета указывают на их функцию:

<?xml version="1.0" encoding="utf-8" ?>
<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.SeparableBlendModesPage"
             Title="Separable Blend Modes">

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

        <Picker x:Name="blendModePicker"
                Title="Blend Mode"
                Margin="10, 0"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKBlendMode}">
                    <x:Static Member="skia:SKBlendMode.Plus" />
                    <x:Static Member="skia:SKBlendMode.Modulate" />
                    <x:Static Member="skia:SKBlendMode.Screen" />
                    <x:Static Member="skia:SKBlendMode.Overlay" />
                    <x:Static Member="skia:SKBlendMode.Darken" />
                    <x:Static Member="skia:SKBlendMode.Lighten" />
                    <x:Static Member="skia:SKBlendMode.ColorDodge" />
                    <x:Static Member="skia:SKBlendMode.ColorBurn" />
                    <x:Static Member="skia:SKBlendMode.HardLight" />
                    <x:Static Member="skia:SKBlendMode.SoftLight" />
                    <x:Static Member="skia:SKBlendMode.Difference" />
                    <x:Static Member="skia:SKBlendMode.Exclusion" />
                    <x:Static Member="skia:SKBlendMode.Multiply" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <Slider x:Name="redSlider"
                MinimumTrackColor="Red"
                MaximumTrackColor="Red"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Slider x:Name="greenSlider"
                MinimumTrackColor="Green"
                MaximumTrackColor="Green"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Slider x:Name="blueSlider"
                MinimumTrackColor="Blue"
                MaximumTrackColor="Blue"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Slider x:Name="graySlider"
                MinimumTrackColor="Gray"
                MaximumTrackColor="Gray"
                Margin="10, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label x:Name="colorLabel"
               HorizontalTextAlignment="Center" />

    </StackLayout>
</ContentPage>

Файл программной части загружает один из ресурсов растрового изображения и рисует его дважды, один раз в верхней половине холста и снова в нижней половине холста:

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

    public SeparableBlendModesPage()
    {
        InitializeComponent();
    }

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

    void OnSliderValueChanged(object sender, ValueChangedEventArgs e)
    {
        if (sender == graySlider)
        {
            redSlider.Value = greenSlider.Value = blueSlider.Value = graySlider.Value;
        }

        colorLabel.Text = String.Format("Color = {0:X2} {1:X2} {2:X2}",
                                        (byte)(255 * redSlider.Value),
                                        (byte)(255 * greenSlider.Value),
                                        (byte)(255 * blueSlider.Value));

        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();

        // Draw bitmap in top half
        SKRect rect = new SKRect(0, 0, info.Width, info.Height / 2);
        canvas.DrawBitmap(bitmap, rect, BitmapStretch.Uniform);

        // Draw bitmap in bottom halr
        rect = new SKRect(0, info.Height / 2, info.Width, info.Height);
        canvas.DrawBitmap(bitmap, rect, BitmapStretch.Uniform);

        // Get values from XAML controls
        SKBlendMode blendMode =
            (SKBlendMode)(blendModePicker.SelectedIndex == -1 ?
                                        0 : blendModePicker.SelectedItem);

        SKColor color = new SKColor((byte)(255 * redSlider.Value),
                                    (byte)(255 * greenSlider.Value),
                                    (byte)(255 * blueSlider.Value));

        // Draw rectangle with blend mode in bottom half
        using (SKPaint paint = new SKPaint())
        {
            paint.Color = color;
            paint.BlendMode = blendMode;
            canvas.DrawRect(rect, paint);
        }
    }
}

В нижней части PaintSurface обработчика прямоугольник рисуется по второй растровой карте с выбранным режимом смешения и выбранным цветом. Измененное растровое изображение в нижней части можно сравнить с исходной растровой картой в верхней части:

Отделяемые режимы смешения

Аддитивные и субтрактивные первичные цвета

Страница "Основные цвета" рисует три перекрывающихся круга красного, зеленого и синего:

Аддитивные основные цвета

Это аддитивные первичные цвета. Сочетания любых двух видов производства цина, пургенты и желтого цвета, а сочетание всех трех является белым.

Эти три круга рисуются с режимомSKBlendMode.Plus, но вы также можете использовать Screenили LightenDifference для того же эффекта. Вот программа:

public class PrimaryColorsPage : ContentPage
{
    bool isSubtractive;

    public PrimaryColorsPage ()
    {
        Title = "Primary Colors";

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

        // Switch between additive and subtractive primaries at tap
        TapGestureRecognizer tap = new TapGestureRecognizer();
        tap.Tapped += (sender, args) =>
        {
            isSubtractive ^= true;
            canvasView.InvalidateSurface();
        };
        canvasView.GestureRecognizers.Add(tap);

        Content = canvasView;
    }

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

        canvas.Clear();

        SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);
        float radius = Math.Min(info.Width, info.Height) / 4;
        float distance = 0.8f * radius;     // from canvas center to circle center
        SKPoint center1 = center + 
            new SKPoint(distance * (float)Math.Cos(9 * Math.PI / 6),
                        distance * (float)Math.Sin(9 * Math.PI / 6));
        SKPoint center2 = center +
            new SKPoint(distance * (float)Math.Cos(1 * Math.PI / 6),
                        distance * (float)Math.Sin(1 * Math.PI / 6));
        SKPoint center3 = center +
            new SKPoint(distance * (float)Math.Cos(5 * Math.PI / 6),
                        distance * (float)Math.Sin(5 * Math.PI / 6));

        using (SKPaint paint = new SKPaint())
        {
            if (!isSubtractive)
            {
                paint.BlendMode = SKBlendMode.Plus; 
                System.Diagnostics.Debug.WriteLine(paint.BlendMode);

                paint.Color = SKColors.Red;
                canvas.DrawCircle(center1, radius, paint);

                paint.Color = SKColors.Lime;    // == (00, FF, 00)
                canvas.DrawCircle(center2, radius, paint);

                paint.Color = SKColors.Blue;
                canvas.DrawCircle(center3, radius, paint);
            }
            else
            {
                paint.BlendMode = SKBlendMode.Multiply
                System.Diagnostics.Debug.WriteLine(paint.BlendMode);

                paint.Color = SKColors.Cyan;
                canvas.DrawCircle(center1, radius, paint);

                paint.Color = SKColors.Magenta;
                canvas.DrawCircle(center2, radius, paint);

                paint.Color = SKColors.Yellow;
                canvas.DrawCircle(center3, radius, paint);
            }
        }
    }
}

Программа включает в TabGestureRecognizerсебя . При нажатии или щелчке экрана программа используется SKBlendMode.Multiply для отображения трех субтрактивных первичных элементов:

Вычитаемые основные цвета

Режим Darken также работает для этого же эффекта.