可分隔混合模式
如您在 SkiaSharp Porter-Duff 混合模式一文中所見,Porter-Duff 混合模式通常會執行裁剪作業。 可分隔的混合模式不同。 可分隔模式會改變影像的個別紅色、綠色和藍色元件。 可分隔混合模式可以混合色彩,以示範紅色、綠色和藍色的組合確實為白色:
變淺和變暗兩種方式
通常有一個有點太深或太淺的位圖。 您可以使用可分隔的混合模式來放大或變暗影像。 事實上,列舉中的 SKBlendMode
兩個可分隔混合模式會命名為 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
屬性設定為 SKBlendMode.Lighten
或SKBlendMode.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)
紅色、綠色和藍色元件會分別處理,這就是為什麼這些混合模式稱為 可 分隔的混合模式。 因此,Dc 和 Sc 的縮寫可用於目的地和來源色彩,而且據了解計算會分別套用至每個紅色、綠色和藍色元件。
下表顯示所有可分隔的混合模式,以及其用途的簡短說明。 第二個數據行會顯示不會產生任何變更的來源色彩:
混合模式 | 沒有變化 | 作業 |
---|---|---|
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
模式繪製,但您也可以使用 Screen
、 Lighten
或 Difference
來取得相同的效果。 以下是程式:
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
也適用於這個相同的效果。