분리 가능한 혼합 모드
SkiaSharp 포터-더프 블렌드 모드 문서에서 보았듯이, 포터-더프 블렌드 모드는 일반적으로 클리핑 작업을 수행합니다. 분리 가능한 혼합 모드는 다릅니다. 분리 가능한 모드는 이미지의 개별 빨강, 녹색 및 파란색 구성 요소를 변경합니다. 분리 가능한 혼합 모드는 색을 혼합하여 빨강, 녹색 및 파랑의 조합이 실제로 흰색임을 보여 줄 수 있습니다.
두 가지 방법 밝게 및 어둡게
다소 어둡거나 너무 밝은 비트맵이 있는 것이 일반적입니다. 분리 가능한 혼합 모드를 사용하여 이미지를 밝게 하거나 어둡게 할 수 있습니다. 실제로 열거형의 분리 가능한 혼합 모드 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
쌍과 Slider
시연 SKBlendMode.Lighten
및 두 번째 쌍은 .를 보여 줍니다 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
처리기는 비트맵에 적합한 사각형을 계산합니다. 처리기는 해당 비트맵을 표시한 다음 해당 속성이 설정된 개체를 사용하여 SKPaint
비트맵 위에 사각형을 BlendMode
SKBlendMode.Lighten
표시합니다 SKBlendMode.Darken
. 속성은 Color
에 기반한 회색 음영입니다 Slider
. 모드의 Lighten
경우 색 범위는 검은색에서 흰색까지이지만 모드의 Darken
경우 흰색에서 검은색까지 다양합니다.
왼쪽에서 오른쪽의 스크린샷은 위쪽 이미지가 밝아지고 아래쪽 이미지가 어두워짐에 따라 점점 더 큰 Slider
값을 표시합니다.
이 프로그램은 분리 가능한 혼합 모드가 사용되는 일반적인 방법을 보여 줍니다. 대상은 일종의 이미지이며 매우 자주 비트맵입니다. 소스는 해당 속성이 분리 가능한 혼합 모드로 BlendMode
설정된 개체를 사용하여 SKPaint
표시되는 사각형입니다. 사각형은 단색(여기와 같이) 또는 그라데이션일 수 있습니다. 투명도는 일반적으로 분리 가능한 혼합 모드에서 사용되지 않습니다 .
이 프로그램을 실험할 때 이러한 두 혼합 모드는 이미지를 균일하게 밝게 하고 어둡게 하지 않는다는 것을 알게 됩니다. 대신, Slider
일종의 임계값을 설정하는 것 같습니다. 예를 들어 모드를 Slider
Lighten
늘리면 이미지의 어두운 영역이 먼저 밝아지고 밝은 영역은 동일하게 기본.
모드의 Lighten
경우 대상 픽셀이 RGB 색 값(Dr, Dg, Db)이고 원본 픽셀이 색(Sr, Sg, Sb)인 경우 출력은 다음과 같이 계산됩니다(또는 Og, Ob).
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· Dc |
Screen |
검정 | 보수 제품 보완: Sc + Dc – Sc· Dc |
Overlay |
회색 | 의 역 HardLight |
Darken |
흰색 | 최소 색: min(Sc, Dc) |
Lighten |
검정 | 최대 색: max(Sc, Dc) |
ColorDodge |
검정 | 원본에 따라 대상을 밝게 합니다. |
ColorBurn |
흰색 | 원본에 따라 대상을 어둡게 합니다. |
HardLight |
회색 | 가혹한 스포트라이트의 효과와 유사 |
SoftLight |
회색 | 소프트 스포트라이트의 효과와 유사 |
Difference |
검정 | 더 밝게에서 어둡게 빼기: Abs(Dc – Sc) |
Exclusion |
검정 | Difference 유사하지만 대비가 낮습니다. |
Multiply |
흰색 | 색을 곱하여 어둡게: Sc· Dc |
이러한 두 소스의 표기법은 동일하지 않지만 W3C 작성 및 혼합 수준 1 사양 및 Skia SkBlendMode 참조에서 더 자세한 알고리즘을 찾을 수 있습니다. 일반적으로 포터-더프 블렌드 모드로 간주되며 Modulate
W3C 사양에 속하지 않는다는 점에 유 Plus
의하세요.
원본이 투명하면 분리 가능한 모든 혼합 모드에 대해 혼합 모드 Modulate
가 적용되지 않습니다. 앞에서 본 것처럼 혼합 모드는 Modulate
알파 채널을 곱하기에 통합합니다. 그렇지 않으면 . Modulate
Multiply
라는 ColorDodge
두 가지 모드를 확인합니다 ColorBurn
. 회피와 화상은 사진 암실 관행에서 비롯되었습니다. 확대기는 네거티브를 통해 빛을 비추어 사진 인쇄를 만듭니다. 빛이 없는 인쇄는 흰색입니다. 더 긴 기간 동안 인쇄에 더 많은 빛이 떨어지면 인쇄가 어두워집니다. 인쇄 제작자는 종종 손이나 작은 물체를 사용하여 일부 빛이 인쇄물의 특정 부분에 떨어지는 것을 차단하여 그 영역을 더 밝게 했습니다. 이것은 회피로 알려져 있습니다. 반대로, 구멍이 있는 불투명한 물질(또는 대부분의 빛을 차단하는 손)을 사용하여 특정 지점에서 더 많은 빛을 전달하여 연소라고 합니다.
닷지 및 번 프로그램은 밝게하고 어둡게 매우 유사합니다. XAML 파일은 동일하지만 다른 요소 이름으로 구조화되고 코드 숨김 파일도 비슷하지만 이러한 두 혼합 모드의 효과는 매우 다릅니다.
작은 Slider
값의 경우 모드는 Lighten
어두운 영역을 먼저 밝게 하는 반면 ColorDodge
더 균일하게 밝아집니다.
이미지 처리 애플리케이션 프로그램은 종종 암실과 마찬가지로 회피 및 연소를 특정 영역으로 제한할 수 있습니다. 그라데이션 또는 다양한 회색 음영이 있는 비트맵으로 이 작업을 수행할 수 있습니다.
분리 가능한 혼합 모드 탐색
분리 가능한 혼합 모드 페이지에서 분리 가능한 모든 혼합 모드를 검사할 수 있습니다. 혼합 모드 중 하나를 사용하여 비트맵 대상과 색이 지정된 사각형 소스를 표시합니다.
XAML 파일은 (혼합 모드 선택) 및 슬라이더 4개를 정의 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
그려지지만, 사용하거나 Difference
동일한 효과에 사용할 Screen
Lighten
수도 있습니다. 프로그램은 다음과 같습니다.
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
동일한 효과에 대해 작동합니다.