Condividi tramite


Modalità di fusione non separabili

Come si è visto nell'articolo Le modalità di fusione separabili SkiaSharp, le modalità di fusione separabili eseguono operazioni sui canali rosso, verde e blu separatamente. Le modalità di fusione non separabili non sono disponibili. Operando sui livelli hue, saturazione e luminosità del colore, le modalità di fusione non separabili possono modificare i colori in modi interessanti:

Esempio non separabile

Modello Hue-Saturation-Luminosity

Per comprendere le modalità di fusione non separabili, è necessario considerare la destinazione e i pixel di origine come colori nel modello Hue-Saturation-Luminosity. (La luminosità è nota anche come Leggerezza.

Il modello di colore HSL è stato illustrato nell'articolo Integrazione con Xamarin.Forms e un programma di esempio in tale articolo consente la sperimentazione con i colori HSL. È possibile creare un SKColor valore usando i valori Hue, Saturation e Luminosità con il metodo statico SKColor.FromHsl .

La tonalità rappresenta la lunghezza d'onda dominante del colore. I valori delle tonalità vanno da 0 a 360 e scorrere i primari additivi e sottrazione: il rosso è il valore 0, il giallo è 60, il verde è 120, il ciano è 180, il blu è 240, magenta è 300 e il ciclo torna al rosso a 360.

Se non esiste un colore dominante, ad esempio il colore è bianco o nero o grigio, allora hue non è definito e in genere impostato su 0.

I valori di saturazione possono variare da 0 a 100 e indicare la purezza del colore. Un valore di saturazione pari a 100 è il colore più puro, mentre i valori inferiori a 100 causano un colore più grigio. Un valore di saturazione pari a 0 restituisce un'ombreggiatura di grigio.

Il valore luminosità (o leggerezza) indica la luminosità del colore. Un valore di luminosità pari a 0 è nero indipendentemente dalle altre impostazioni. Analogamente, un valore di luminosità pari a 100 è bianco.

Il valore HSL (0, 100, 50) è il valore RGB (FF, 00, 00), che è rosso puro. Il valore HSL (180, 100, 50) è il valore RGB (00, FF, FF), ciano puro. Man mano che la saturazione è diminuita, il componente del colore dominante viene ridotto e gli altri componenti vengono aumentati. A un livello di saturazione pari a 0, tutti i componenti sono uguali e il colore è una sfumatura grigia. Diminuire la luminosità per andare al nero; aumentare la luminosità per andare a bianco.

Modalità blend in dettaglio

Come le altre modalità di fusione, le quattro modalità di fusione non separabili comportano una destinazione (che spesso è un'immagine bitmap) e un'origine, che spesso è un singolo colore o una sfumatura. Le modalità di fusione combinano i valori Hue, Saturation e Luminosità dalla destinazione e dall'origine:

Modalità blend Componenti dall'origine Componenti dalla destinazione
Hue Hue Saturazione e luminosità
Saturation Saturazione Tonalità e luminosità
Color Tonalità e saturazione Luminosità
Luminosity Luminosità Tonalità e saturazione

Vedere la specifica W3C Compositing and Blending Level 1 (Composizione W3C e Blending Level 1 ) per gli algoritmi.

La pagina Modalità blend non separabili contiene un oggetto Picker per selezionare una di queste modalità di fusione e tre Slider visualizzazioni per selezionare un colore 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>

Per risparmiare spazio, le tre Slider visualizzazioni non vengono identificate nell'interfaccia utente del programma. È necessario ricordare che l'ordine è Hue, Saturation e Luminosità. Due Label visualizzazioni nella parte inferiore della pagina mostrano i valori di colore HSL e RGB.

Il file code-behind carica una delle risorse bitmap, visualizza il più grande possibile nell'area di disegno e quindi copre l'area di disegno con un rettangolo. Il colore del rettangolo si basa sulle tre Slider visualizzazioni e la modalità di fusione è quella selezionata in 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);
        }
    }
}

Si noti che il programma non visualizza il valore del colore HSL come selezionato dai tre dispositivi di scorrimento. Crea invece un valore di colore da tali dispositivi di scorrimento e quindi usa il ToHsl metodo per ottenere i valori Hue, Saturation e Luminosità. Questo perché il FromHsl metodo converte un colore HSL in un colore RGB, archiviato internamente nella SKColor struttura. Il ToHsl metodo converte da RGB a HSL, ma il risultato non sarà sempre il valore originale.

Ad esempio, FromHsl converte il valore HSL (180, 50, 0) nel colore RGB (0, 0, 0) perché è Luminosity zero. Il ToHsl metodo converte il colore RGB (0, 0, 0) nel colore HSL (0, 0, 0) perché i valori Hue e Saturation sono irrilevanti. Quando si usa questo programma, è preferibile visualizzare la rappresentazione del colore HSL usato dal programma anziché quello specificato con i dispositivi di scorrimento.

La SKBlendModes.Hue modalità blend usa il livello Hue dell'origine mantenendo i livelli di saturazione e luminosità della destinazione. Quando si testa questa modalità di fusione, i dispositivi di scorrimento della saturazione e della luminosità devono essere impostati su un valore diverso da 0 o 100 perché in questi casi la tonalità non è definita in modo univoco.

Modalità blend non separabili - Tonalità

Quando si usa impostare il dispositivo di scorrimento su 0 (come con lo screenshot iOS a sinistra), tutto diventa rossastro. Ma questo non significa che l'immagine è completamente assente di verde e blu. Ovviamente ci sono ancora sfumature grigie presenti nel risultato. Ad esempio, il colore RGB (40, 40, C0) equivale al colore HSL (240, 50, 50). Hue è blu, ma il valore di saturazione pari a 50 indica che sono presenti anche componenti rossi e verdi. Se Hue è impostato su 0 con SKBlendModes.Hue, il colore HSL è (0, 50, 50), ovvero il colore RGB (C0, 40, 40). Ci sono ancora componenti blu e verde, ma ora il componente dominante è rosso.

La SKBlendModes.Saturation modalità blend combina il livello di saturazione dell'origine con hue e luminosità della destinazione. Come la Tonalità, la saturazione non è ben definita se la luminosità è 0 o 100. In teoria, qualsiasi impostazione di luminosità tra questi due estremi dovrebbe funzionare. Tuttavia, l'impostazione Luminosità sembra influire sul risultato più che dovrebbe. Impostare la luminosità su 50 ed è possibile vedere come impostare il livello di saturazione dell'immagine:

Modalità di fusione non separabili - Saturazione

È possibile usare questa modalità di fusione per aumentare la saturazione del colore di un'immagine opaca oppure ridurre la saturazione fino a zero (come nello screenshot iOS a sinistra) per un'immagine risultante composta solo da sfumature grigie.

La SKBlendModes.Color modalità blend mantiene la luminosità della destinazione, ma usa hue e saturazione dell'origine. Anche in questo caso, ciò implica che qualsiasi impostazione del dispositivo di scorrimento Luminosità da qualche parte tra gli estremi dovrebbe funzionare.

Modalità di fusione non separabili - Colore

A breve verrà visualizzata un'applicazione di questa modalità di fusione.

Infine, la SKBlendModes.Luminosity modalità blend è l'opposto di SKBlendModes.Color. Mantiene la tonalità e la saturazione della destinazione, ma usa la luminosità dell'origine. La Luminosity modalità di fusione è la più misteriosa del batch: i dispositivi di scorrimento Hue e Saturazione influiscono sull'immagine, ma anche a luminosità media, l'immagine non è distinta:

Modalità blend non separabili - Luminosità

In teoria, aumentare o diminuire la luminosità di un'immagine dovrebbe renderla più chiara o scura. Questo esempio di proprietà Luminosità o questa definizione di Enumerazione SKBlendMode può essere di interesse.

In genere non è consigliabile usare una delle modalità di fusione non separabili con un'origine costituita da un singolo colore applicato all'intera immagine di destinazione. L'effetto è troppo grande. Si vuole limitare l'effetto a una parte dell'immagine. In tal caso, l'origine incorporerà probabilmente la transparanza, o forse l'origine sarà limitata a un elemento grafico più piccolo.

Matte per una modalità separabile

Ecco una delle bitmap incluse come risorsa nell'esempio. Il nome file è Banana.jpg:

Banana Monkey

È possibile creare un opaco che comprende solo la banana. Si tratta anche di una risorsa nell'esempio. Il nome file è BananaMatte.png:

Banana Matte

A parte la forma di banana nera, il resto della bitmap è trasparente.

La pagina Blue Banana usa tale matto per modificare la tonalità e la saturazione della banana che la scimmia sta tenendo, ma per cambiare nient'altro nell'immagine.

Nella classe seguente BlueBananaPage la bitmap Banana.jpg viene caricata come campo. Il costruttore carica il BananaMatte.png bitmap come matteBitmap oggetto, ma non mantiene tale oggetto oltre il costruttore. Viene invece creata una terza bitmap denominata blueBananaBitmap . L'oggetto matteBitmap viene disegnato su seguito da un SKPaint oggetto con il relativo Color valore impostato su blueBananaBitmap blu e il relativo BlendMode valore impostato su SKBlendMode.SrcIn. Il blueBananaBitmap rimane per lo più trasparente ma con un'immagine blu pura solida della 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);
        }
    }
}

Il PaintSurface gestore disegna la bitmap con la scimmia che tiene la banana. Questo codice è seguito dalla visualizzazione di blueBananaBitmap con SKBlendMode.Color. Sulla superficie della banana, la tonalità e la saturazione di ogni pixel vengono sostituite dal blu solido, che corrisponde a un valore di tonalità 240 e un valore di saturazione pari a 100. La Luminosità, tuttavia, rimane invariata, il che significa che la banana continua ad avere una trama realistica nonostante il suo nuovo colore:

Banana blu

Provare a modificare la modalità blend in SKBlendMode.Saturation. La banana rimane gialla, ma è un giallo più intenso. In un'applicazione reale, questo potrebbe essere un effetto più auspicabile che trasformare il blu della banana.