共用方式為


不可分隔的混合模式

如您在SkiaSharp可分隔混合模式一文中所見,可分隔混合模式會分別在紅色、綠色和藍色通道上執行作業。 不可分隔的混合模式不會。 藉由根據色彩的 Hue、飽和度和亮度層級運作,不可分隔的混合模式可以透過有趣的方式改變色彩:

不可分隔的範例

Hue-Saturation-Luminosity 模型

若要瞭解不可分隔的混合模式,必須將目的地和來源圖元視為 Hue-Saturation-Luminosity 模型中的色彩。 (亮度也稱為光性。

HSL 色彩模型已在與整合Xamarin.Forms一文中討論,以及該文章中的範例程式允許使用 HSL 色彩進行實驗。 您可以使用 Hue、飽和度和 Luminosity 值搭配靜態SKColor.FromHsl方法來建立SKColor值。

Hue 代表色彩的主要濃度。 Hue 值的範圍從 0 到 360,並迴圈到加法和減法初選:紅色是 0,黃色是 60,綠色是 120,青色是 180,藍色為 240,洋紅是 300,迴圈回到紅色在 360。

如果沒有佔主導地位的色彩,例如,色彩是白色或黑色或灰色陰影,則 Hue 是未定義且通常設定為 0。

飽和度值的範圍可以從 0 到 100,並表示色彩的純潔度。 飽和度值為 100 是純色,而低於 100 的值會導致色彩變灰。 飽和度值為 0 會導致灰色的陰影。

Luminosity (或 Lightness) 值表示色彩的亮度。 不論其他設定為何,0 的亮度值為黑色。 同樣地,100 的亮度值為白色。

HSL 值 (0, 100, 50) 是 RGB 值 (FF, 00, 00), 這是純紅色。 HSL 值 (180, 100, 50) 是 RGB 值 (00, FF, FF, FF), 純青色。 隨著飽和度降低,主要色彩元件會減少,並增加其他元件。 在飽和度層級為 0 時,所有元件都相同,且色彩為灰色陰影。 減少發光度以移至黑色;增加亮度去白色。

混合模式詳細

與其他混合模式一樣,四種不可分隔的混合模式涉及目的地(通常是位圖影像)和來源,這通常是單一色彩或漸層。 混合模式結合了目的地和來源的 Hue、飽和度和亮度值:

混合模式 來源的元件 來自目的地的元件
Hue Hue 飽和度和亮度
Saturation 飽和度 Hue 和 Luminosity
Color 色調和飽和度 光度
Luminosity 光度 色調和飽和度

如需演算法,請參閱 W3C Compositing 和 Blending Level 1 規格。

[ 不可分隔混合模式 ] 頁面包含 , Picker 可選取其中一種混合模式,以及三 Slider 個檢視來選取 HSL 色彩:

<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.NonSeparableBlendModesPage"
             Title="Non-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.Hue" />
                    <x:Static Member="skia:SKBlendMode.Saturation" />
                    <x:Static Member="skia:SKBlendMode.Color" />
                    <x:Static Member="skia:SKBlendMode.Luminosity" />
                </x:Array>
            </Picker.ItemsSource>

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

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

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

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

        <StackLayout Orientation="Horizontal">
            <Label x:Name="hslLabel"
                   HorizontalOptions="CenterAndExpand" />

            <Label x:Name="rgbLabel"
                   HorizontalOptions="CenterAndExpand" />

        </StackLayout>
    </StackLayout>
</ContentPage>

為了節省空間,程式的使用者介面中不會識別這三 Slider 個檢視。 您必須記住順序為 Hue、飽和度和亮度。 頁面底部的兩 Label 個檢視會顯示 HSL 和 RGB 色彩值。

程序代碼後置檔案會載入其中一個點陣圖資源、在畫布上顯示盡可能大,然後使用矩形來涵蓋畫布。 矩形色彩是以三 Slider 個檢視為基礎,而混合模式是在 中 Picker選取的:

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

    public NonSeparableBlendModesPage()
    {
        InitializeComponent();
    }

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

    void OnSliderValueChanged(object sender, ValueChangedEventArgs e)
    {
        // Calculate new color based on sliders
        color = SKColor.FromHsl((float)hueSlider.Value,
                                (float)satSlider.Value,
                                (float)lumSlider.Value);

        // Use labels to display HSL and RGB color values
        color.ToHsl(out float hue, out float sat, out float lum);

        hslLabel.Text = String.Format("HSL = {0:F0} {1:F0} {2:F0}",
                                      hue, sat, lum);

        rgbLabel.Text = String.Format("RGB = {0:X2} {1:X2} {2:X2}",
                                      color.Red, color.Green, color.Blue);

        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();
        canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);

        // Get blend mode from Picker
        SKBlendMode blendMode =
            (SKBlendMode)(blendModePicker.SelectedIndex == -1 ?
                                        0 : blendModePicker.SelectedItem);

        using (SKPaint paint = new SKPaint())
        {
            paint.Color = color;
            paint.BlendMode = blendMode;
            canvas.DrawRect(info.Rect, paint);
        }
    }
}

請注意,程式不會顯示三個滑桿所選取的 HSL 色彩值。 相反地,它會從這些滑桿建立色彩值,然後使用 ToHsl 方法來取得 Hue、飽和度和 Luminosity 值。 這是因為 FromHsl 方法會將 HSL 色彩轉換成 RGB 色彩,而 RGB 色彩會儲存在 SKColor 結構內部。 方法 ToHsl 會從 RGB 轉換成 HSL,但結果不一定是原始值。

例如, FromHsl 將 HSL 值 (180, 50, 0) 轉換為 RGB 色彩 (0, 0, 0, 0),因為 Luminosity 是零。 方法 ToHsl 會將 RGB 色彩 (0, 0, 0, 0) 轉換為 HSL 色彩 (0, 0, 0, 0),因為 Hue 和飽和度值無關。 使用此程式時,最好是看到程式所使用的 HSL 色彩表示,而不是您使用滑桿指定的色彩。

SKBlendModes.Hue混合模式會使用來源的 Hue 層級,同時保留目的地的飽和度和亮度層級。 當您測試此混合模式時,飽和度和亮度滑桿必須設定為 0 或 100 以外的專案,因為在這些情況下,Hue 並非唯一定義。

不可分隔的混合模式 - 色調

當您使用 將滑桿設定為0時(如同左側的iOS螢幕快照),一切都會變成紅色。 但這並不表示影像完全不存在綠色和藍色。 很明顯,結果中仍有灰色陰影。 例如,RGB 色彩 (40, 40, C0) 相當於 HSL 色彩 (240, 50, 50)。 Hue 為藍色,但 50 的飽和度值也表示也有紅色和綠色元件。 如果 Hue 設定為 0,HSL SKBlendModes.Hue色彩為 (0, 50, 50, 50),這是 RGB 色彩 (C0, 40, 40)。 仍有藍色和綠色元件,但現在佔主導地位的元件是紅色。

SKBlendModes.Saturation混合模式結合了來源的飽和度層級與目的地的 Hue 和亮度。 和 Hue 一樣,如果亮度為 0 或 100,則不會定義飽和度。 理論上,這兩個極端之間的任何亮度設定都應該奏效。 不過,Luminosity 設定似乎比結果更會影響結果。 將亮度設定為 50,您可以看到如何設定圖片的飽和度層級:

不可分離的混合模式 - 飽和度

您可以使用此混合模式來增加沉悶影像的色彩飽和度,或將飽和度減少到零(如左側的 iOS 螢幕快照),以產生只由灰色陰影組成的影像。

SKBlendModes.Color混合模式會保留目的地的亮度,但會使用來源的 Hue 和飽和度。 同樣地,這表示極端之間的任何 Luminosity 滑桿設定都應該正常運作。

不可分隔的混合模式 - 色彩

您很快就會看到此混合模式的應用程式。

最後, SKBlendModes.Luminosity 混合模式與相反 SKBlendModes.Color。 它會保留目的地的 Hue 和飽和度,但會使用來源的亮度。 Luminosity混合模式是最神秘的批次:色調和飽和度滑桿會影響影像,但即使在中等亮度,影像也不明顯:

不可分隔的混合模式 - 亮度

理論上,增加或減少影像的亮度應該使它變淺或更暗。 此 Luminosity 屬性範例 或此 SKBlendMode Enum 定義 可能感興趣。

通常不是您想要使用其中一個不可分隔的混合模式,以及套用至整個目的地影像的單一色彩的來源。 效果太好了。 您想要將效果限制為影像的一部分。 在此情況下,來源可能會納入交易,或來源可能會限製為較小的圖形。

可分隔模式的啞光

以下是範例中作為資源的其中一個位圖。 檔案名為 Banana.jpg

香蕉猴

可以建立一個啞光,只包含香蕉。 這也是範例中的資源。 檔案名為 BananaMatte.png

香蕉馬特

除了黑色香蕉形狀之外,位陣圖的其餘部分是透明的。

藍色香蕉頁面會使用該啞光來改變猴子持有的香蕉色調和飽和度,但要改變影像中沒有其他內容。

在下列 BlueBananaPage 類別中 ,Banana.jpg 位圖會載入為欄位。 建構函式會將 BananaMatte.png 位圖載入為 matteBitmap 物件,但不會將該物件保留在建構函式之外。 相反地,會建立名為 blueBananaBitmap 的第三個位圖。 在 matteBitmapblueBananaBitmap 繪製 ,後面接著 SKPaint ,其 Color 設定為藍色,並將其 BlendMode 設定為 SKBlendMode.SrcInblueBananaBitmap仍然大部分是透明的,但具有純藍色的香蕉圖像:

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

    SKBitmap blueBananaBitmap;

    public BlueBananaPage()
    {
        Title = "Blue Banana";

        // Load banana matte bitmap (black on transparent)
        SKBitmap matteBitmap = BitmapExtensions.LoadBitmapResource(
            typeof(BlueBananaPage),
            "SkiaSharpFormsDemos.Media.BananaMatte.png");

        // Create a bitmap with a solid blue banana and transparent otherwise
        blueBananaBitmap = new SKBitmap(matteBitmap.Width, matteBitmap.Height);

        using (SKCanvas canvas = new SKCanvas(blueBananaBitmap))
        {
            canvas.Clear();
            canvas.DrawBitmap(matteBitmap, new SKPoint(0, 0));

            using (SKPaint paint = new SKPaint())
            {
                paint.Color = SKColors.Blue;
                paint.BlendMode = SKBlendMode.SrcIn;
                canvas.DrawPaint(paint);
            }
        }

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

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

        canvas.Clear();

        canvas.DrawBitmap(bitmap, info.Rect, BitmapStretch.Uniform);

        using (SKPaint paint = new SKPaint())
        {
            paint.BlendMode = SKBlendMode.Color;
            canvas.DrawBitmap(blueBananaBitmap,
                              info.Rect,
                              BitmapStretch.Uniform,
                              paint: paint);
        }
    }
}

處理程式 PaintSurface 會繪製位圖,其中猴子持有香蕉。 此程式代碼後面接著使用 的顯示blueBananaBitmapSKBlendMode.Color。 在香蕉表面,每個圖元的 Hue 和飽和度都會由純藍色取代,其對應到色調值為 240,飽和度值為 100。 然而,亮度仍然相同,這意味著香蕉繼續有一個現實的紋理,儘管它的新顏色:

藍色香蕉

請嘗試將混合模式變更為 SKBlendMode.Saturation。 香蕉仍然黃色,但它是一個更強烈的黃色。 在現實生活中的應用程式中,這可能比將香蕉變成藍色更理想的效果。