Modalità di fusione separabili

Download Sample Scaricare l'esempio

Come si è visto nell'articolo Le modalità di fusione SkiaSharp Porter-Duff, le modalità di fusione Porter-Duff in genere eseguono operazioni di ritaglio. Le modalità di fusione separabili sono diverse. Le modalità separabili modificano i singoli componenti di colore rosso, verde e blu di un'immagine. Le modalità di fusione separabili possono combinare il colore per dimostrare che la combinazione di rosso, verde e blu è effettivamente bianco:

Primary Colors

Lucere e scurire due modi

È comune avere una bitmap troppo scura o troppo chiara. È possibile usare le modalità di fusione separabili per rendere più chiara o oscurata l'immagine. Infatti, due delle modalità di fusione separabili nell'enumerazione SKBlendMode sono denominate Lighten e Darken.

Queste due modalità sono illustrate nella pagina Lighten e Darken . Il file XAML crea un'istanza di due SKCanvasView oggetti e due Slider visualizzazioni:

<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>

La prima e Slider la dimostrazione SKCanvasViewSKBlendMode.Lighten e la seconda coppia illustra SKBlendMode.Darken. Le due Slider visualizzazioni condividono lo stesso ValueChanged gestore e le due SKCanvasView condividono lo stesso PaintSurface gestore. Entrambi i gestori eventi controllano quale oggetto sta attivando l'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);
        }
    }
}

Il PaintSurface gestore calcola un rettangolo adatto per la bitmap. Il gestore visualizza tale bitmap e quindi visualizza un rettangolo sulla bitmap usando un SKPaint oggetto con la relativa BlendMode proprietà impostata su SKBlendMode.Lighten o SKBlendMode.Darken. La Color proprietà è una sfumatura grigia basata su Slider. Per la Lighten modalità, il colore varia da nero a bianco, ma per la Darken modalità varia da bianco a nero.

Gli screenshot da sinistra a destra mostrano valori sempre più grandi Slider man mano che l'immagine superiore diventa più chiara e l'immagine inferiore diventa più scura:

Lighten and Darken

Questo programma illustra il modo normale in cui vengono usate le modalità di fusione separabili: la destinazione è un'immagine di qualche tipo, molto spesso una bitmap. L'origine è un rettangolo visualizzato usando un SKPaint oggetto con la relativa BlendMode proprietà impostata su una modalità di fusione separabile. Il rettangolo può essere un colore a tinta unita (come qui) o una sfumatura. La trasparenza non viene in genere usata con le modalità di fusione separabili.

Durante l'esperimento con questo programma, scoprirai che queste due modalità di fusione non illuminano e scurino l'immagine in modo uniforme. Al contrario, sembra Slider impostare una soglia di qualche tipo. Ad esempio, man mano che si aumenta per Slider la Lighten modalità, le aree più scure dell'immagine ottengono prima luce mentre le aree più chiare rimangono invariate.

Per la Lighten modalità, se il pixel di destinazione è il valore del colore RGB (Dr, Dg, Db) e il pixel di origine è il colore (Sr, Sg, Sb), l'output è (Or, Og, Ob) calcolato come segue:

Or = max(Dr, Sr) Og = max(Dg, Sg) Ob = max(Db, Sb)

Per rosso, verde e blu separatamente, il risultato è maggiore della destinazione e dell'origine. Ciò produce l'effetto di illuminare prima le aree scure della destinazione.

La Darken modalità è simile, ad eccezione del fatto che il risultato è minore della destinazione e dell'origine:

Or = min(Dr, Sr) Og = min(Dg, Sg) Ob = min(Db, Sb)

I componenti rosso, verde e blu sono ognuno gestito separatamente, motivo per cui queste modalità di fusione vengono definite modalità di fusione separabili . Per questo motivo, le abbreviazioni Dc e Sc possono essere usate per i colori di destinazione e di origine ed è chiaro che i calcoli si applicano a ognuno dei componenti rosso, verde e blu separatamente.

La tabella seguente illustra tutte le modalità di fusione separabili con brevi spiegazioni delle operazioni eseguite. La seconda colonna mostra il colore di origine che non produce alcuna modifica:

Modalità blend Nessuna modifica Operazione
Plus Nero Alleggerisce aggiungendo colori: Sc + Dc
Modulate Bianco Scuri per moltiplicazione dei colori: Sc· Dc
Screen Nero Complementi prodotti di complementi: Sc + Dc – Sc· Dc
Overlay Grigio Inverso di HardLight
Darken Bianco Minimo colori: min(Sc, Dc)
Lighten Nero Numero massimo di colori: max(Sc, Dc)
ColorDodge Nero Destinazione Brightens in base all'origine
ColorBurn Bianco Destinazione scura in base all'origine
HardLight Grigio Simile all'effetto dei riflettori duri
SoftLight Grigio Simile all'effetto di contenuti in evidenza soft
Difference Nero Sottrae il più scuro dal più chiaro: Abs(Dc – Sc)
Exclusion Nero Simile al contrasto inferiore ma simile a Difference
Multiply Bianco Scuri per moltiplicazione dei colori: Sc· Dc

Gli algoritmi più dettagliati sono disponibili nella specifica W3C Compositing and Blending Level 1 e nel riferimento Skia SkBlendMode, anche se la notazione in queste due origini non è la stessa. Tenere presente che Plus è comunemente considerato come modalità di fusione Porter-Duff e Modulate non fa parte della specifica W3C.

Se l'origine è trasparente, per tutte le modalità di fusione separabili ad eccezione Modulatedi , la modalità blend non ha alcun effetto. Come si è visto in precedenza, la Modulate modalità blend incorpora il canale alfa nella moltiplicazione. In caso contrario, Modulate ha lo stesso effetto di Multiply.

Si notino le due modalità denominate ColorDodge e ColorBurn. Le parole schivare e bruciare hanno avuto origine in pratiche fotografiche di camera oscura. Un ingranditore rende una stampa fotografica splendendo la luce attraverso un negativo. Senza luce, la stampa è bianca. La stampa diventa più scura man mano che più luce cade sulla stampa per un periodo di tempo più lungo. I produttori di stampa spesso utilizzavano una mano o un piccolo oggetto per impedire ad alcune delle luci di cadere su una determinata parte della stampa, rendendo quell'area più leggera. Questo è noto come schivamento. Al contrario, materiale opaco con un foro in esso (o mani che bloccano la maggior parte della luce) può essere usato per indirizzare più luce in un particolare punto per scurirlo, chiamato bruciare.

Il programma Dodge e Burn è molto simile a Lighten e Darken. Il file XAML è strutturato allo stesso modo ma con nomi di elementi diversi e il file code-behind è simile, ma l'effetto di queste due modalità di fusione è piuttosto diverso:

Dodge and Burn

Per i valori di piccole dimensioni Slider , la Lighten modalità illumina prima le aree scure, mentre ColorDodge si illumina in modo più uniforme.

I programmi dell'applicazione di elaborazione delle immagini spesso consentono di schivare e bruciare di essere limitati a aree specifiche, proprio come in una stanza oscura. Questa operazione può essere eseguita da sfumature o da una bitmap con sfumature diverse di grigio.

Esplorazione delle modalità di fusione separabili

La pagina Modalità blend separabili consente di esaminare tutte le modalità di fusione separabili. Visualizza una destinazione bitmap e un'origine rettangolo colorata usando una delle modalità di fusione.

Il file XAML definisce un oggetto Picker (per selezionare la modalità blend) e quattro dispositivi di scorrimento. I primi tre dispositivi di scorrimento consentono di impostare i componenti rosso, verde e blu dell'origine. Il quarto dispositivo di scorrimento è progettato per eseguire l'override di tali valori impostando un'ombreggiatura grigia. I singoli dispositivi di scorrimento non vengono identificati, ma i colori indicano la loro funzione:

<?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>

Il file code-behind carica una delle risorse bitmap e lo disegna due volte, una volta nella metà superiore dell'area di disegno e di nuovo nella metà inferiore dell'area di disegno:

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);
        }
    }
}

Verso la parte inferiore del PaintSurface gestore, viene disegnato un rettangolo sulla seconda bitmap con la modalità di fusione selezionata e il colore selezionato. È possibile confrontare la bitmap modificata nella parte inferiore con la bitmap originale nella parte superiore:

Separable Blend Modes

Colori primari additivi e sottrazione

La pagina Colori primari disegna tre cerchi sovrapposti di rosso, verde e blu:

Additive Primary Colors

Questi sono i colori primari additivi. Le combinazioni di due producono ciano, magenta e giallo, e una combinazione di tutti e tre è bianca.

Questi tre cerchi vengono disegnati con la SKBlendMode.Plus modalità , ma è anche possibile usare Screen, Lighteno Difference per lo stesso effetto. Ecco il programma:

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);
            }
        }
    }
}

Il programma include un oggetto TabGestureRecognizer. Quando si tocca o si fa clic sullo schermo, il programma usa SKBlendMode.Multiply per visualizzare le tre primarie sottrazione:

Subtractive Primary Colors

La Darken modalità funziona anche per questo stesso effetto.