Modalità di fusione separabili
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:
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 SKCanvasView
SKBlendMode.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:
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 Modulate
di , 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:
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:
Colori primari additivi e sottrazione
La pagina Colori primari disegna tre cerchi sovrapposti di rosso, verde e blu:
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
, Lighten
o 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:
La Darken
modalità funziona anche per questo stesso effetto.