分離不可能なブレンド モード
「SkiaSharp の分離可能なブレンド モード」で説明したように、分離可能なブレンド モードでは、赤、緑、青の各チャンネルに対して個別に操作が行われます。 分離不可能なブレンド モードでは行われません。 色の色相、彩度、明度のレベルに基づいて動作することにより、分離不可能なブレンド モードは興味深い方法で色を変更できます。
HSL (色相 - 再度 - 明度) モデル
分離不可能なブレンド モードを理解するには、変換先ピクセルとソース ピクセルを HSL (色相 - 再度 - 明度) モデルの色として扱う必要があります。 (明度は輝度とも呼ばれます)。
HSL カラー モデルについては、「Xamarin.Forms との統合」で説明しました。この記事のサンプル プログラムを使用すると、HSL 色を使用した実験を行うことができます。 静的メソッド SKColor.FromHsl
では、色相、彩度、明度の値を使用して SKColor
値を作成できます。
色相は、色の主要な波長を表します。 色相の値の範囲は 0 から 360 で、加法と減算の原色を循環します。赤は値 0、黄色は 60、緑は 120、シアンは 180、青は 240、マゼンタは 300 であり、サイクルは 360 で赤に戻ります。
たとえば、主要な色がない場合 (色が白または黒または灰色の網掛けである場合)、色相は未定義であり、通常は 0 に設定されます。
彩度の値の範囲は 0 から 100 で、色の純度を示します。 彩度の値が 100 の場合は最も純度の高い色になり、100 より小さい値を指定すると、色がより灰色に変わります。 彩度の値を 0 にすると、灰色の網掛けになります。
明度 (または輝度) の値は、色の明るさを示します。 他の設定に関係なく、明度の値を 0 にすると、黒になります。 同様に、明度の値を 100 にすると、白になります。
HSL 値 (0, 100, 50) は RGB 値 (FF, 00, 00) であり、純粋な赤色です。 HSL 値 (180,100, 50) は、RGB 値 (00、FF、FF) であり、純粋なシアンです。 彩度を下げると、主要な色成分が減少し、他の成分が増加します。 彩度レベルが 0 の場合、すべてのコンポーネントは同じになり、色は灰色の網掛けになります。 明度を下げると、黒になり、明度を上げると、白になります。
ブレンド モードの詳細
他のブレンド モードと同様に、分離できない 4 つのブレンド モードには、変換先 (多くの場合、ビットマップ イメージ) とソース (多くの場合、単色またはグラデーション) が含まれます。 ブレンド モードでは、変換先とソースの色相、彩度、明度の値が結合されます。
ブレンド モード | ソースのコンポーネント | 変換先のコンポーネント |
---|---|---|
Hue |
Hue | 彩度と明度 |
Saturation |
[鮮やかさ] | 色相と明度 |
Color |
色相と彩度 | 明度 |
Luminosity |
明度 | 色相と彩度 |
アルゴリズムについては、W3Cの「合成とブレンド レベル 1」の仕様を参照してください。
Non-Separable Blend Modes ページには、次のいずれかのブレンド モードを選択するための Picker
と、HSL カラーを選択するための 3 つの Slider
ビューが含まれています。
<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>
領域を節約するために、3 つの Slider
ビューはプログラムのユーザー インターフェイスでは識別されません。 順序は色相、彩度、明度であることを覚えておく必要があります。 ページの下部にある 2 つの Label
ビューには、HSL と RGB のカラー値が表示されます。
分離コード ファイルは、ビットマップ リソースの 1 つを読み込み、キャンバスにできる限り大きく表示し、キャンバスを四角形で覆います。 四角形の色は 3 つの 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);
}
}
}
このプログラムは、3 つのスライダーで選択された HSL カラー値を表示しないことに注意してください。 代わりに、これらのスライダーからカラー値を作成し、ToHsl
メソッドを使用して色相、彩度、明度の値を取得します。 これは、この FromHsl
メソッドが HSL カラーを RGB カラーに変換し、内部的に SKColor
構造体に格納されるためです。 この ToHsl
メソッドは RGB から HSL に変換されますが、結果が常に元の値であるとは限りません。
たとえば、FromHsl
は HSL 値 (180, 50, 0) を RGB カラー (0, 0, 0) に変換します。これは Luminosity
が 0 であるためです。 この ToHsl
メソッドは、RGB カラー (0, 0, 0) を HSL カラー (0, 0, 0) に変換します。これは、色相と彩度の値は関係がないためです。 このプログラムを使用する場合は、スライダーで指定した色ではなく、プログラムが使用している HSL カラーの表現を確認することをお勧めします。
SKBlendModes.Hue
ブレンド モードでは、変換先の彩度と明度レベルを維持しながら、ソースの色相レベルを使用します。 このブレンド モードをテストする場合、彩度と明度のスライダーは 0 または 100 以外に設定する必要があります。その場合、色相は一意に定義されないためです。
スライダーを 0 に設定すると (左側の iOS のスクリーンショットと同様)、すべてが赤く変わります。 しかし、これは画像に緑と青がまったく存在しないという意味ではありません。 明らかに、結果にはまだ灰色の網掛けがあります。 たとえば、RGB カラー (40, 40, C0) は、HSL カラー (240, 50, 50) と同じです。 色相は青ですが、彩度の値が 50 の場合は、赤と緑のコンポーネントも存在することを示します。 色相が SKBlendModes.Hue
で 0 に設定されている場合、HSL カラーは (0, 50, 50) であり、これは RGB カラー (C0, 40, 40) です。 まだ青と緑のコンポーネントがありますが、主要なコンポーネントは赤になりました。
SKBlendModes.Saturation
ブレンド モードでは、ソースの彩度レベルと変換先の色相と明度が結合されます。 色相と同様に、明度が 0 または 100 の場合、彩度は明確に定義されません。 理論上、これら 2 つの両極端の間にある明度の設定は機能するはずです。 しかし、明度の設定は、必要以上に結果に影響を与えるようです。 次のように明度を 50 に設定すると、図の彩度レベルがどのように設定されるかを確認できます。
このブレンド モードを使用して、曇ったような画像の色の彩度を上げたり、灰色の網掛けのみで構成された結果の画像の彩度をゼロ (左側の iOS のスクリーンショットのように) に減らしたりできます。
SKBlendModes.Color
ブレンド モードでは変換先の明度が保持されますが、ソースの色相と彩度が使用されます。 繰り返しますが、これは、両極端の間のどこかに明度スライダーを設定すると、機能することを意味します。
このブレンド モードの使い方は、すぐに確認することになります。
最後に、SKBlendModes.Luminosity
ブレンド モードは SKBlendModes.Color
の逆です。 変換先の色相と彩度は保持されますが、ソースの明度が使用されます。 Luminosity
ブレンド モードはバッチの中で最も複雑です。色相スライダーと彩度スライダーは画像に影響しますが、明度が中程度の場合でも、画像ははっきりしません。
理論的には、画像の明度を増減させると、明るさや暗さが増します。 この Luminosity プロパティの例、またはこの SKBlendMode 列挙型の定義が参考になるかもしれません。
一般に、変換先の画像全体に適用される単色で構成されるソースに、分離不可能なブレンド モードの 1 つを使用することはありません。 この効果はあまりにも大きすぎます。 効果を画像の一部に制限する必要があります。 その場合、ソースにはおそらく透明度が組み込まれるか、ソースがより小さいグラフィックに制限される可能性があります。
分離可能モードのマット
サンプルにリソースとして含まれるビットマップの 1 つを次に示します。 ファイル名は Banana.jpg です。
バナナだけを含むマットを作成できます。 これもサンプルのリソースです。 ファイル名は BananaMatte.png です。
黒いバナナの形とは別に、ビットマップの残りの部分は透明になります。
Blue Banana ページでは、そのマットを使用して、サルが持っているバナナの色相と彩度を変更しますが、画像内の他の部分は変更されません。
次の BlueBananaPage
クラスでは、Banana.jpg ビットマップがフィールドとして読み込まれます。 コンストラクターは BananaMatte.png ビットマップを matteBitmap
オブジェクトとして読み込みますが、コンストラクターを超えてそのオブジェクトを保持することはありません。 代わりに、blueBananaBitmap
という名前の 3 番目のビットマップが作成されます。 matteBitmap
は、blueBananaBitmap
上に描画され、次に Color
が青に設定され、BlendMode
が SKBlendMode.SrcIn
に設定された SKPaint
が描画されます。 blueBananaBitmap
はほとんど透明のままですが、バナナの純粋な青い画像を伴います。
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
ハンドラーは、バナナを持っているサルを含むビットマップを描画します。 このコードの後に、SKBlendMode.Color
を使用した blueBananaBitmap
が表示されます。 バナナの表面上では、各ピクセルの色相と彩度は青の単色に置き換えられます。これは色相の値 240 と彩度の値 100 に対応します。 しかし、明度は同じままであり、つまり、バナナは新しい色にもかかわらず現実的なテクスチャを持ち続けます。
ブレンド モードを SKBlendMode.Saturation
に変更してみてください。 バナナは黄色のままですが、より激しい黄色になります。 実際のアプリケーションでは、バナナを青くするよりも望ましい効果かもしれません。