Os modos de mistura não separáveis
Como você viu no artigo SkiaSharp separable blend modes, os modos de mesclagem separáveis executam operações nos canais vermelho, verde e azul separadamente. Os modos de mesclagem não separáveis não. Ao operar sobre os níveis de cor, Saturação e Luminosidade, os modos de mistura não separáveis podem alterar as cores de maneiras interessantes:
O modelo Matiz-Saturação-Luminosidade
Para entender os modos de mistura não separáveis, é necessário tratar os pixels de destino e de origem como cores no modelo Matiz-Saturação-Luminosidade. (A luminosidade também é referida como leveza.)
O modelo de cores HSL foi discutido no artigo Integrando com Xamarin.Formse um programa de exemplo nesse artigo permite a experimentação com cores HSL. Você pode criar um SKColor
valor usando os valores Hue, Saturation e Luminosity com o método estático SKColor.FromHsl
.
A tonalidade representa o comprimento de onda dominante da cor. Os valores de matiz variam de 0 a 360 e percorrem as primárias aditivas e subtrativas: vermelho é o valor 0, amarelo é 60, verde é 120, ciano é 180, azul é 240, magenta é 300 e o ciclo volta para vermelho em 360.
Se não houver uma cor dominante — por exemplo, a cor é branca ou preta ou um tom de cinza — então a tonalidade é indefinida e geralmente definida como 0.
Os valores de Saturação podem variar de 0 a 100 e indicam a pureza da cor. Um valor de Saturação de 100 é a cor mais pura, enquanto valores inferiores a 100 fazem com que a cor se torne mais acinzentada. Um valor de Saturação de 0 resulta em um tom de cinza.
O valor de Luminosidade (ou Luminosidade) indica o quão brilhante é a cor. Um valor de luminosidade de 0 é preto independentemente das outras configurações. Da mesma forma, um valor de Luminosidade de 100 é branco.
O valor HSL (0, 100, 50) é o valor RGB (FF, 00, 00), que é vermelho puro. O valor HSL (180, 100, 50) é o valor RGB (00, FF, FF), ciano puro. À medida que a Saturação é diminuída, o componente de cor dominante é diminuído e os outros componentes são aumentados. Em um nível de saturação de 0, todos os componentes são os mesmos e a cor é um tom cinza. Diminua a luminosidade para ir para preto; aumentar a luminosidade para ir para o branco.
Os modos de mistura em detalhes
Como os outros modos de mesclagem, os quatro modos de mesclagem não separáveis envolvem um destino (que geralmente é uma imagem bitmap) e uma origem, que geralmente é uma única cor ou um gradiente. Os modos de mesclagem combinam valores de Matiz, Saturação e Luminosidade do destino e da origem:
Modo de mistura | Componentes da origem | Componentes do destino |
---|---|---|
Hue |
Matiz | Saturação e Luminosidade |
Saturation |
Saturação | Matiz e Luminosidade |
Color |
Matiz e Saturação | Luminosidade |
Luminosity |
Luminosidade | Matiz e Saturação |
Consulte a especificação W3C Compositing and Blending Level 1 para os algoritmos.
A página Modos de mesclagem não separáveis contém um Picker
para selecionar um desses modos de mesclagem e três Slider
modos de exibição para selecionar uma cor 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>
Para economizar espaço, as três Slider
exibições não são identificadas na interface do usuário do programa. Você precisará lembrar que a ordem é Matiz, Saturação e Luminosidade. Duas Label
exibições na parte inferior da página mostram os valores de cores HSL e RGB.
O arquivo code-behind carrega um dos recursos de bitmap, exibe o maior tamanho possível na tela e, em seguida, cobre a tela com um retângulo. A cor do retângulo é baseada nas três Slider
visualizações e o modo de mesclagem é o selecionado no 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);
}
}
}
Observe que o programa não exibe o valor de cor HSL como selecionado pelos três controles deslizantes. Em vez disso, ele cria um valor de cor desses controles deslizantes e, em seguida, usa o ToHsl
método para obter os valores de Matiz, Saturação e Luminosidade. Isso ocorre porque o método converte FromHsl
uma cor HSL em uma cor RGB, que é armazenada internamente na SKColor
estrutura. O ToHsl
método converte de RGB para HSL, mas o resultado nem sempre será o valor original.
Por exemplo, FromHsl
converte o valor HSL (180, 50, 0) para a cor RGB (0, 0, 0) porque o Luminosity
é zero. O ToHsl
método converte a cor RGB (0, 0, 0) para a cor HSL (0, 0, 0) porque os valores de matiz e saturação são irrelevantes. Ao usar este programa, é melhor que você veja a representação da cor HSL que o programa está usando em vez da que você especificou com os controles deslizantes.
O SKBlendModes.Hue
modo de mesclagem usa o nível de matiz da fonte, mantendo os níveis de saturação e luminosidade do destino. Quando você testa esse modo de mesclagem, os controles deslizantes de saturação e luminosidade devem ser definidos como algo diferente de 0 ou 100 porque, nesses casos, o matiz não é definido exclusivamente.
Quando você usa definir o controle deslizante como 0 (como na captura de tela do iOS à esquerda), tudo fica avermelhado. Mas isso não significa que a imagem esteja totalmente ausente de verde e azul. Obviamente ainda há tons de cinza presentes no resultado. Por exemplo, a cor RGB (40, 40, C0) é equivalente à cor HSL (240, 50, 50). O Hue é azul, mas o valor de saturação de 50 indica que há componentes vermelhos e verdes também. Se a Matiz estiver definida como 0 com SKBlendModes.Hue
, a cor HSL será (0, 50, 50), que é a cor RGB (C0, 40, 40). Ainda há componentes azuis e verdes, mas agora o componente dominante é o vermelho.
O SKBlendModes.Saturation
modo de mistura combina o nível de Saturação da fonte com a Matiz e a Luminosidade do destino. Como a Matiz, a Saturação não é bem definida se a Luminosidade é 0 ou 100. Em teoria, qualquer configuração de luminosidade entre esses dois extremos deveria funcionar. No entanto, a configuração de Luminosidade parece afetar o resultado mais do que deveria. Defina a luminosidade para 50, e você pode ver como você pode definir o nível de saturação da imagem:
Você pode usar esse modo de mesclagem para aumentar a Saturação de cores de uma imagem sem brilho ou pode diminuir a Saturação até zero (como na captura de tela do iOS à esquerda) para uma imagem resultante composta apenas por tons de cinza.
O SKBlendModes.Color
modo de mesclagem mantém a Luminosidade do destino, mas usa a Matiz e a Saturação da fonte. Novamente, isso implica que qualquer configuração do controle deslizante Luminosity em algum lugar entre os extremos deve funcionar.
Você verá um aplicativo desse modo de mesclagem em breve.
Finalmente, o SKBlendModes.Luminosity
modo de mistura é o oposto de SKBlendModes.Color
. Ele mantém a tonalidade e a saturação do destino, mas usa a luminosidade da fonte. O Luminosity
modo de mistura é o mais misterioso do lote: os controles deslizantes Hue e Saturation afetam a imagem, mas mesmo em Luminosidade média, a imagem não é distinta:
Em teoria, aumentar ou diminuir a luminosidade de uma imagem deve torná-la mais clara ou mais escura. Este exemplo de propriedade Luminosity ou esta definição de SKBlendMode Enum pode ser de interesse.
Geralmente, não é o caso de você querer usar um dos modos de mesclagem não separáveis com uma origem que consiste em uma única cor aplicada a toda a imagem de destino. O efeito é muito grande. Você vai querer restringir o efeito a uma parte da imagem. Nesse caso, a fonte provavelmente incorporará transparência, ou talvez a fonte seja limitada a um gráfico menor.
Um fosco para um modo separável
Aqui está um dos bitmaps incluídos como um recurso no exemplo. O nome do arquivo é Banana.jpg:
É possível criar um fosco que engloba apenas a banana. Este também é um recurso no exemplo. O nome do arquivo é BananaMatte.png:
Além da forma de banana preta, o resto do bitmap é transparente.
A página Banana Azul usa esse fosco para alterar a tonalidade e a saturação da banana que o macaco está segurando, mas para não mudar mais nada na imagem.
Na classe a seguirBlueBananaPage
, o bitmap Banana.jpg é carregado como um campo. O construtor carrega o bitmap BananaMatte.png como o matteBitmap
objeto, mas ele não retém esse objeto além do construtor. Em vez disso, um terceiro bitmap chamado blueBananaBitmap
é criado. O matteBitmap
é desenhado em blueBananaBitmap
seguido por um SKPaint
com seu Color
conjunto para azul e seu BlendMode
conjunto para SKBlendMode.SrcIn
. Os blueBananaBitmap
restos em sua maioria transparentes, mas com uma imagem azul pura sólida da banana:
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);
}
}
}
O PaintSurface
manipulador desenha o bitmap com o macaco segurando a banana. Esse código é seguido pela exibição de blueBananaBitmap
com SKBlendMode.Color
. Sobre a superfície da banana, a tonalidade e saturação de cada pixel é substituída pelo azul sólido, que corresponde a um valor de matiz de 240 e um valor de saturação de 100. A luminosidade, no entanto, permanece a mesma, o que significa que a banana continua a ter uma textura realista, apesar de sua nova cor:
Tente alterar o modo de mesclagem para SKBlendMode.Saturation
. A banana permanece amarela, mas é um amarelo mais intenso. Em uma aplicação da vida real, este pode ser um efeito mais desejável do que tornar a banana azul.