Partilhar via


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:

Amostra não separável

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.

Modos de mistura não separáveis - matiz

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:

Modos de mistura não separáveis - Saturação

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.

Modos de mesclagem não separáveis - Cor

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:

Modos de mistura não separáveis - Luminosidade

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:

Macaco da Bananeira

É possível criar um fosco que engloba apenas a banana. Este também é um recurso no exemplo. O nome do arquivo é BananaMatte.png:

Banana Matte

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:

Banana Azul

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.