Os modos de mistura separáveis
Como você viu no artigo SkiaSharp Porter-Duff blend modes, os modos de mesclagem Porter-Duff geralmente executam operações de recorte. Os modos de mistura separáveis são diferentes. Os modos separáveis alteram os componentes individuais de cor vermelho, verde e azul de uma imagem. Os modos de mesclagem separáveis podem misturar cores para demonstrar que a combinação de vermelho, verde e azul é realmente branca:
Clarear e escurecer de duas maneiras
É comum ter um bitmap um pouco escuro demais ou muito claro. Você pode usar modos de mesclagem separáveis para clarear ou escurecer a imagem. De fato, dois dos modos de mesclagem separáveis na SKBlendMode
enumeração são nomeados Lighten
e Darken
.
Esses dois modos são demonstrados na página Lighten e Darken . O arquivo XAML instancia dois SKCanvasView
objetos e dois Slider
modos de exibição:
<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>
O primeiro SKCanvasView
e Slider
demonstra SKBlendMode.Lighten
e o segundo par demonstra SKBlendMode.Darken
. Os dois Slider
modos de exibição compartilham o mesmo ValueChanged
manipulador, e os dois SKCanvasView
compartilham o mesmo PaintSurface
manipulador. Ambos os manipuladores de eventos verificam qual objeto está disparando o evento:
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);
}
}
}
O PaintSurface
manipulador calcula um retângulo adequado para o bitmap. O manipulador exibe esse bitmap e, em seguida, exibe um retângulo sobre o bitmap usando um SKPaint
objeto com sua BlendMode
propriedade definida como SKBlendMode.Lighten
ou SKBlendMode.Darken
. A Color
propriedade é um tom de cinza baseado no Slider
. Para o Lighten
modo, a cor varia de preto a branco, mas para o Darken
modo varia de branco a preto.
As capturas de tela da esquerda para a direita mostram valores cada vez maiores Slider
à medida que a imagem superior fica mais clara e a imagem inferior fica mais escura:
Este programa demonstra a maneira normal em que os modos de mistura separáveis são usados: O destino é uma imagem de algum tipo, muitas vezes um bitmap. A origem é um retângulo exibido usando um SKPaint
objeto com sua BlendMode
propriedade definida para um modo de mesclagem separável. O retângulo pode ser uma cor sólida (como é aqui) ou um gradiente. A transparência geralmente não é usada com os modos de mistura separáveis.
Ao experimentar este programa, você descobrirá que esses dois modos de mesclagem não clareiam e escurecem a imagem uniformemente. Em vez disso, o Slider
parece definir um limite de algum tipo. Por exemplo, à medida que você aumenta o Slider
para o Lighten
modo, as áreas mais escuras da imagem ficam claras primeiro, enquanto as áreas mais claras permanecem as mesmas.
Para o Lighten
modo, se o pixel de destino for o valor de cor RGB (Dr, Dg, Db) e o pixel de origem for a cor (Sr, Sg, Sb), a saída será (Ou, Og, Ob) calculada da seguinte maneira:
Or = max(Dr, Sr)
Og = max(Dg, Sg)
Ob = max(Db, Sb)
Para vermelho, verde e azul separadamente, o resultado é o maior do destino e da origem. Isso produz o efeito de clarear as áreas escuras do destino primeiro.
O Darken
modo é semelhante, exceto que o resultado é o menor do destino e da origem:
Or = min(Dr, Sr)
Og = min(Dg, Sg)
Ob = min(Db, Sb)
Os componentes vermelho, verde e azul são tratados separadamente, e é por isso que esses modos de mistura são chamados de modos de mistura separáveis . Por esse motivo, as abreviaturas Dc e Sc podem ser usadas para as cores de destino e origem, e entende-se que os cálculos se aplicam a cada um dos componentes vermelho, verde e azul separadamente.
A tabela a seguir mostra todos os modos de mesclagem separáveis com breves explicações do que eles fazem. A segunda coluna mostra a cor de origem que não produz nenhuma alteração:
Modo de mistura | Nenhuma alteração | Operação |
---|---|---|
Plus |
Preto | Ilumina adicionando cores: Sc + Dc |
Modulate |
Branca | Escurece multiplicando cores: Sc· Dc |
Screen |
Preto | Produto de complementos: Sc + Dc – Sc· Dc |
Overlay |
Cinza | Inverso de HardLight |
Darken |
Branca | Mínimo de cores: min (Sc, Dc) |
Lighten |
Preto | Máximo de cores: max (Sc, Dc) |
ColorDodge |
Preto | Ilumina o destino com base na origem |
ColorBurn |
Branca | Destino de Darkens com base na origem |
HardLight |
Cinza | Semelhante ao efeito de holofote severo |
SoftLight |
Cinza | Semelhante ao efeito do holofote macio |
Difference |
Preto | Subtrai o mais escuro do mais claro: Abs(Dc – Sc) |
Exclusion |
Preto | Contraste semelhante, Difference mas mais baixo |
Multiply |
Branca | Escurece multiplicando cores: Sc· Dc |
Algoritmos mais detalhados podem ser encontrados na especificação W3C Compositing and Blending Level 1 e na Skia SkBlendMode Reference, embora a notação nessas duas fontes não seja a mesma. Tenha em mente que Plus
é comumente considerado como um modo de mistura Porter-Duff e Modulate
não faz parte da especificação W3C.
Se a origem for transparente, então para todos os modos de mesclagem separáveis, exceto Modulate
, o modo de mesclagem não terá efeito. Como você viu anteriormente, o Modulate
modo de mistura incorpora o canal alfa na multiplicação. Caso contrário, Modulate
tem o mesmo efeito que Multiply
.
Observe os dois modos nomeados ColorDodge
e ColorBurn
. As palavras esquivar e queimar tiveram origem em práticas fotográficas de câmara escura. Um ampliador faz uma impressão fotográfica brilhando luz através de um negativo. Sem luz, a estampa é branca. A impressão fica mais escura à medida que mais luz incide sobre a impressão por um longo período de tempo. Os fabricantes de impressão geralmente usavam uma mão ou um pequeno objeto para impedir que parte da luz caísse sobre uma determinada parte da impressão, tornando essa área mais leve. Isso é conhecido como esquiva. Por outro lado, material opaco com um buraco (ou mãos bloqueando a maior parte da luz) poderia ser usado para direcionar mais luz em um determinado local para escurecê-lo, chamado de queima.
O programa Dodge and Burn é muito semelhante ao Lighten e Darken. O arquivo XAML é estruturado da mesma forma, mas com nomes de elementos diferentes, e o arquivo code-behind também é bastante semelhante, mas o efeito desses dois modos de mesclagem é bem diferente:
Para valores pequenos Slider
, o Lighten
modo clareia primeiro as áreas escuras, enquanto ColorDodge
clareia de forma mais uniforme.
Programas de aplicativos de processamento de imagens geralmente permitem que a esquiva e a gravação sejam restritas a áreas específicas, assim como em uma câmara escura. Isso pode ser feito por gradientes ou por um bitmap com tons variados de cinza.
Explorando os modos de mistura separáveis
A página Modos de mesclagem separáveis permite examinar todos os modos de mesclagem separáveis. Ele exibe um destino de bitmap e uma fonte de retângulo colorida usando um dos modos de mesclagem.
O arquivo XAML define um Picker
(para selecionar o modo de mesclagem) e quatro controles deslizantes. Os três primeiros controles deslizantes permitem definir os componentes vermelho, verde e azul da origem. O quarto controle deslizante destina-se a substituir esses valores definindo um tom cinza. Os controles deslizantes individuais não são identificados, mas as cores indicam sua função:
<?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>
O arquivo code-behind carrega um dos recursos de bitmap e o desenha duas vezes, uma na metade superior da tela e novamente na metade inferior da tela:
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);
}
}
}
Na parte inferior do PaintSurface
manipulador, um retângulo é desenhado sobre o segundo bitmap com o modo de mesclagem selecionado e a cor selecionada. Você pode comparar o bitmap modificado na parte inferior com o bitmap original na parte superior:
Cores primárias aditivas e subtrativas
A página Cores Primárias desenha três círculos sobrepostos de vermelho, verde e azul:
Estas são as cores primárias aditivas. Combinações de quaisquer dois produzem ciano, magenta e amarelo, e uma combinação de todos os três é branca.
Esses três círculos são desenhados com o SKBlendMode.Plus
modo, mas você também pode usar Screen
, Lighten
ou Difference
para o mesmo efeito. Aqui está o programa:
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);
}
}
}
}
O programa inclui um TabGestureRecognizer
arquivo . Quando você toca ou clica na tela, o programa usa SKBlendMode.Multiply
para exibir as três primárias subtrativas:
O Darken
modo também funciona para esse mesmo efeito.