可分离的混合模式The separable blend modes

下载示例下载示例Download Sample Download the sample

本文中所示 SkiaSharp Porter Duff 混合模式,Porter Duff 的混合模式通常执行剪切操作。As you saw in the article SkiaSharp Porter-Duff blend modes, the Porter-Duff blend modes generally perform clipping operations. 可分离的混合模式有不同。The separable blend modes are different. 可分离模式更改图像的单独的红色、 绿色和蓝色颜色组件。The separable modes alter the individual red, green, and blue color components of an image. 可分离的混合模式可以混合使用颜色来演示的红色、 绿色和蓝色的组合是确实白色:Separable blend modes can mix color to demonstrate that the combination of red, green, and blue is indeed white:

基本色Primary Colors

淡化和加深两种方法Lighten and darken two ways

它是很常见的是某种程度上的位图太暗或太亮。It is common to have a bitmap that is somewhat too dark or too light. 可以使用可分离的混合模式变淡或变暗 imabe。You can use separable blend modes to lighten or darken the imabe. 实际上,两个中的可分离 blend 模式 SKBlendMode 枚举命名LightenDarkenIndeed, two of the separable blend modes in the SKBlendMode enumeration are named Lighten and Darken.

中演示了这两种模式变淡和变暗页。These two modes are demonstrated in the Lighten and Darken page. XAML 文件实例化两个SKCanvasView对象和两个Slider视图:The XAML file instantiates two SKCanvasView objects and two Slider views:

<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>

第一个SKCanvasViewSlider演示SKBlendMode.Lighten,并将第二对演示SKBlendMode.DarkenThe first SKCanvasView and Slider demonstrate SKBlendMode.Lighten and the second pair demonstrates SKBlendMode.Darken. 这两个Slider视图都共享相同ValueChanged处理程序和两个SKCanvasView共用同一个PaintSurface处理程序。The two Slider views share the same ValueChanged handler, and the two SKCanvasView share the same PaintSurface handler. 这两个事件处理程序检查哪些对象将触发此事件:Both event handlers check which object is firing the event:

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处理程序计算适用于位图的矩形。The PaintSurface handler calculates a rectangle suitable for the bitmap. 该处理程序将显示该位图,然后显示的对位图使用矩形SKPaint对象,其BlendMode属性设置为SKBlendMode.LightenSKBlendMode.DarkenThe handler displays that bitmap and then displays a rectangle over the bitmap using an SKPaint object with its BlendMode property set to SKBlendMode.Lighten or SKBlendMode.Darken. Color属性是基于灰影SliderThe Color property is a gray shade based on the Slider. 有关Lighten模式下,颜色范围是从黑色到白色,但对于Darken其范围为从白色到黑色的模式。For the Lighten mode, the color ranges from black to white, but for the Darken mode it ranges from white to black.

从左到右的屏幕截图显示变大Slider顶部图像获取较浅和底部图像获取较暗的值:The screenshots from left to right show increasingly larger Slider values as the top image gets lighter and the bottom image gets darker:

淡化和加深Lighten and Darken

此程序演示使用分离混合模式的正常方式:目标是某种类型的图像, 通常是位图。This program demonstrates the normal way in which the separable blend modes are used: The destination is an image of some sort, very often a bitmap. 源是使用显示的矩形SKPaint对象,其BlendMode属性设置为可分离混合模式。The source is a rectangle displayed using an SKPaint object with its BlendMode property set to a separable blend mode. 矩形 (因为它是此处) 可以是纯色或渐变。The rectangle can be a solid color (as it is here) or a gradient. 透明度是_不_通常适用于可分离的混合模式。Transparency is not generally used with the separable blend modes.

在试验与此程序时,你会发现这两个混合模式不变淡并且均匀使图像变暗。As you experiment with this program, you'll discover that these two blend modes do not lighten and darken the image uniformly. 相反,Slider似乎设置某种类型的阈值。Instead, the Slider seems to set a threshold of some sort. 例如,当您增加SliderLighten模式下,图像的较暗的区域获得 light 第一个较浅区域保持不变。For example, as you increase the Slider for the Lighten mode, the darker areas of the image get light first while the lighter areas remain the same.

有关Lighten模式下,如果目标像素的 RGB 颜色值 (灾难恢复,Dg,Db),源像素的颜色 (Sr,Sg,Sb),则输出为 (或 Og,Ob) 按以下方式计算:For the Lighten mode, if the destination pixel is the RGB color value (Dr, Dg, Db), and the source pixel is the color (Sr, Sg, Sb), then the output is (Or, Og, Ob) calculated as follows:

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

另外,对于红色、 绿色和蓝色,结果是目标和源中的较大的。For red, green, and blue separately, the result is the greater of the destination and source. 这将产生第一次变革目标深色区域的效果。This produces the effect of lightening the dark areas of the destination first.

Darken模式,类似,但结果是较小的目标和源:The Darken mode is similar except that the result is the lesser of the destination and source:

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

红色、 绿色和蓝色组件是每个单独处理的这正是这些混合模式被称为_可分离_混合模式。The red, green, and blue components are each handled separately, which is why these blend modes are referred to as the separable blend modes. 出于此原因,缩写DcSc可用于目标和源颜色和理解,计算将应用于每个红色、 绿色和蓝色组件分开。For this reason, the abbreviations Dc and Sc can be used for the destination and source colors, and it's understood that calculations apply to each of the red, green, and blue components separately.

下表显示了所有可分离混合模式下与它们的用途的简短说明。The following table shows all the separable blend modes with brief explanations of what they do. 第二列显示了生成未更改的源颜色:The second column shows the source color that produces no change:

混合模式Blend Mode 无更改No change 操作Operation
Plus 黑色Black 通过添加颜色变亮:Sc + DcLightens by adding colors: Sc + Dc
Modulate 白色White 通过按颜色加深:Sc·DcDarkens by multiplying colors: Sc·Dc
Screen 黑色Black 补充的产品:Sc + Dc – Sc ·台Complements product of complements: Sc + Dc – Sc·Dc
Overlay 灰色Gray 函数的反函数 HardLightInverse of HardLight
Darken 白色White 最小的颜色: 最小值 (Sc、 Dc)Minimum of colors: min(Sc, Dc)
Lighten 黑色Black 最大的颜色: 最大值 (Sc、 Dc)Maximum of colors: max(Sc, Dc)
ColorDodge 黑色Black 使更亮,基于源的目标Brightens destination based on source
ColorBurn 白色White 基于源的目标会变暗Darkens destination based on source
HardLight 灰色Gray 类似于 harsh 聚焦的效果Similar to effect of harsh spotlight
SoftLight 灰色Gray 类似于软聚焦的效果Similar to effect of soft spotlight
Difference 黑色Black 从较浅的减去较暗的:Abs (Dc – Sc)Subtracts the darker from the lighter: Abs(Dc – Sc)
Exclusion 黑色Black 类似于Difference但较低的对比度Similar to Difference but lower contrast
Multiply 白色White 通过按颜色加深:Sc·DcDarkens by multiplying colors: Sc·Dc

可以在 w3c 中找到更详细的算法合成和混合级别 1 规范和 Skia SkBlendMode 引用,但这些两个源中的表示法不相同。More detailed algorithms can be found in the W3C Compositing and Blending Level 1 specification and the Skia SkBlendMode Reference, although the notation in these two sources is not the same. 请记住PlusPorter Duff 混合模式,通常件和Modulate不是 W3C 规范的一部分。Keep in mind that Plus is commonly regarded as a Porter-Duff blend mode, and Modulate is not part of the W3C specification.

如果源是透明的然后为所有可分离混合模式除Modulate,混合模式下不起作用。If the source is transparent, then for all the separable blend modes except Modulate, the blend mode has no effect. 正如您看到更早版本,Modulate混合模式下包含该乘法运算中的 alpha 通道。As you've seen earlier, the Modulate blend mode incorporates the alpha channel in the multiplication. 否则为Modulate具有相同的效果MultiplyOtherwise, Modulate has the same effect as Multiply.

请注意,名为两种模式ColorDodgeColorBurnNotice the two modes named ColorDodge and ColorBurn. 单词_避开_并_刻录_源自于摄影暗室实践。The words dodge and burn originated in photographic darkroom practices. 放大器照射通过为负,从而使照片打印。An enlarger makes a photographic print by shining light through a negative. 无光,则输出为白色。With no light, the print is white. 打印获取变暗详细光是打印较长的时间。The print gets darker as more light falls on the print for a longer period of time. 打印制造商通常用于手动或小对象阻止某些侵扰上打印,使较浅此区域的特定部分光线。Print-makers often used a hand or small object to block some of the light from falling on a certain part of the print, making that area lighter. 这称为_逃避_。This is known as dodging. 相反,使用它 (或阻止大多数光的手) 一个孔的不透明材料无法用于直接中变暗,它名为某个特定点的详细 light_刻录_。Conversely, opaque material with a hole in it (or hands blocking most of the light) could be used to direct more light in a particular spot to darken it, called burning.

避开和 Burn计划是非常类似于变淡和变暗The Dodge and Burn program is very similar to Lighten and Darken. XAML 文件是结构化相同但不同的元素名称和代码隐藏文件同样非常相似,但这两个混合模式的效果是完全不同:The XAML file is structured the same but with different element names, and the code-behind file is likewise quite similar, but the effect of these two blend modes is quite different:

避开和 BurnDodge and Burn

对于小型Slider值,Lighten模式变浅深色区域第一次,而ColorDodge变浅更均匀。For small Slider values, the Lighten mode lightens dark areas first, while ColorDodge lightens more uniformly.

图像处理应用程序通常允许逃避和刻录限制到特定区域,就像在暗室中。Image-processing application programs often allow dodging and burning to be restricted to specific areas, just like in a darkroom. 此操作可以完成通过渐变,或使用不同的灰影的位图。This can be accomplished by gradients, or by a bitmap with varying shades of gray.

探索可分离的混合模式Exploring the separable blend modes

Blend 的可分离模式页面允许您检查所有可分离的混合模式。The Separable Blend Modes page allows you to examine all the separable blend modes. 它显示位图目标和使用的混合模式之一的有色的矩形源。It displays a bitmap destination and a colored rectangle source using one of the blend modes.

XAML 文件定义Picker(若要选择混合模式) 和四个滑块。The XAML file defines a Picker (to select the blend mode) and four sliders. 前三个滑块,可以设置源的红色、 绿色和蓝色组件。The first three sliders let you set the red, green, and blue components of the source. 第四个滑块旨在通过设置灰色阴影覆盖这些值。The fourth slider is intended to override those values by setting a gray shade. 无法识别各个滑块,但颜色指示其功能:The individual sliders are not identified, but colors indicate their function:

<?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>

代码隐藏文件加载位图资源之一,并绘制它两次,一次在画布的上半部分中,再次在底部画布的下半部分:The code-behind file loads one of the bitmap resources and draws it twice, once in the top half of the canvas and again in the bottom half of the canvas:

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上使用所选的混合模式和所选的颜色的第二个位图绘制处理程序中,一个矩形。Towards the bottom of the PaintSurface handler, a rectangle is drawn over the second bitmap with the selected blend mode and the selected color. 您可以比较底部与原始位图顶部修改后的位图:You can compare the modified bitmap at the bottom with the original bitmap at the top:

可分离的混合模式Separable Blend Modes

累加性和删减主色Additive and subtractive primary colors

种基本色页绘制红色、 绿色和蓝色的三个重叠圆圈:The Primary Colors page draws three overlapping circles of red, green, and blue:

累加性种基本色Additive Primary Colors

这些是累加性的主要颜色。These are the additive primary colors. 任何两个的组合会产生青、 品红和黄,和的所有三个组合为白色。Combinations of any two produce cyan, magenta, and yellow, and a combination of all three is white.

与绘制这些三个圆圈SKBlendMode.Plus模式,但您还可以使用ScreenLighten,或Difference为相同的效果。These three circles are drawn with the SKBlendMode.Plus mode, but you can also use Screen, Lighten, or Difference for the same effect. 下面是该程序:Here's the program:

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);
            }
        }
    }
}

该程序包括TabGestureRecognizerThe program includes a TabGestureRecognizer. 点击或单击屏幕时,该程序使用SKBlendMode.Multiply以显示三个删减主副本:When you tap or click the screen, the program uses SKBlendMode.Multiply to display the three subtractive primaries:

删减种基本色Subtractive Primary Colors

Darken模式也适用于此相同的效果。The Darken mode also works for this same effect.