共用方式為


可分隔混合模式

如您在 SkiaSharp Porter-Duff 混合模式一文中所見,Porter-Duff 混合模式通常會執行裁剪作業。 可分隔的混合模式不同。 可分隔模式會改變影像的個別紅色、綠色和藍色元件。 可分隔混合模式可以混合色彩,以示範紅色、綠色和藍色的組合確實為白色:

原色

變淺和變暗兩種方式

通常有一個有點太深或太淺的位圖。 您可以使用可分隔的混合模式來放大或變暗影像。 事實上,列舉中的 SKBlendMode 兩個可分隔混合模式會命名為 LightenDarken

這兩種模式會在 [淺色] 和 [深色 ] 頁面中示範。 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.LightenSlider第二組示範 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 屬性設定為 SKBlendMode.LightenSKBlendMode.Darken的物件,在點陣圖SKPaint上顯示矩形。 屬性 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)

紅色、綠色和藍色元件會分別處理,這就是為什麼這些混合模式稱為 分隔的混合模式。 因此,DcSc 的縮寫可用於目的地和來源色彩,而且據了解計算會分別套用至每個紅色、綠色和藍色元件。

下表顯示所有可分隔的混合模式,以及其用途的簡短說明。 第二個數據行會顯示不會產生任何變更的來源色彩:

混合模式 沒有變化 作業
Plus 黑色 新增色彩來減輕光線:Sc + Dc
Modulate 白色 將色彩乘以變暗:Sc•直流
Screen 黑色 補碼的補碼產品:Sc + Dc – Sc·直流
Overlay 灰色 反向 HardLight
Darken 白色 色彩下限:min(Sc,Dc)
Lighten 黑色 色彩上限:max(Sc、Dc)
ColorDodge 黑色 根據來源亮化目的地
ColorBurn 白色 根據來源將目的地變深
HardLight 灰色 類似於嚴酷聚光燈的效果
SoftLight 灰色 類似於柔聚光燈的效果
Difference 黑色 從較淺的減去較深: Abs(Dc – Sc)
Exclusion 黑色 Difference類似於 但較低的對比
Multiply 白色 將色彩乘以變暗:Sc•直流

如需更詳細的演算法,請參閱 W3C Compositing 和 Blending Level 1 規格和 Skia SkBlendMode 參考,雖然這兩個來源中的表示法不同。 請記住, Plus 通常被視為 Porter-Duff 混合模式,且 Modulate 不屬於 W3C 規格的一部分。

如果來源是透明的,則所有可分隔的混合模式除外 Modulate,混合模式沒有任何作用。 如您稍早所見, Modulate 混合模式會在乘法中包含Alpha色板。 否則, Modulate 的效果與 Multiply相同。

請注意名為和ColorBurn的兩種模式ColorDodge。 這些話 躲避燒傷 源於攝影的黑暗室做法。 放大鏡通過陰光照亮來製作攝影印刷品。 沒有光線,列印是白色的。 列印會變暗,因為列印上的光線越長一段時間。 印刷製造商經常使用手或小物體來阻止某些光線落在列印的某些部分,使該區域變輕。 這稱為 「擷取」。 相反地,不透明的材料與一個洞在它(或手阻止大多數光)可以用來引導更多的光線在特定的地方變黑,稱為 燃燒

道奇和伯恩計劃非常類似於 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 模式繪製,但您也可以使用 ScreenLightenDifference 來取得相同的效果。 以下是程式:

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 也適用於這個相同的效果。