Režimy mixu porter-Duff
Režimy mixu Porter-Duff jsou pojmenovány po Thomasi Porterovi a Tomu Duffovi, který vyvinul algebra kompozitingu při práci pro Lucasfilm. Jejich papír Kompoziting Digital Images byl publikován v červenci 1984 vydání počítačové grafiky, stránky 253 až 259. Tyto režimy blendu jsou nezbytné pro kompozitování, což je sestavení různých obrázků do složeného scény:
Koncepty porter-Duff
Předpokládejme, že černěný obdélník zabírá levou a horní dvě třetiny povrchu displeje:
Tato oblast se nazývá cíl nebo někdy pozadí nebo pozadí.
Chcete nakreslit následující obdélník, což je stejná velikost cíle. Obdélník je průhledný s výjimkou zmodré oblasti, která zabírá vpravo a dole dvě třetiny:
Tomu se říká zdroj nebo někdy popředí.
Když zdroj zobrazíte v cíli, očekáváte toto:
Průhledné pixely zdroje umožňují, aby se pozadí zobrazilo, zatímco modré zdrojové pixely zakrývají pozadí. To je normální případ, a to je uvedeno ve SkiaSharp jako SKBlendMode.SrcOver
. Tato hodnota je výchozí nastavení BlendMode
vlastnosti při SKPaint
prvním vytvoření instance objektu.
Pro jiný efekt je ale možné určit jiný režim mixu. Pokud zadáte SKBlendMode.DstOver
, pak v oblasti, kde zdroj a cíl protínají, se místo zdroje zobrazí cíl:
Režim SKBlendMode.DstIn
blendu zobrazí pouze oblast, ve které cíl a zdroj protínají cílovou barvou:
Režim kombinace (výhradního SKBlendMode.Xor
or) způsobí, že se tyto dvě oblasti překrývají, nic se nezobrazí:
Barevné cílové a zdrojové obdélníky efektivně rozdělují plochu zobrazení do čtyř jedinečných oblastí, které se dají obarvit různými způsoby, které odpovídají přítomnosti cílových a zdrojových obdélníků:
Obdélníky v pravém horním a levém dolním rohu jsou vždy prázdné, protože cíl i zdroj jsou v těchto oblastech průhledné. Cílová barva zabírá levou horní oblast, takže oblast může být buď barevná s cílovou barvou, nebo vůbec. Podobně zdrojová barva zabírá oblast vpravo dole, takže oblast může být barevná se zdrojovou barvou nebo vůbec. Průnik cíle a zdroje uprostřed může být barevný s cílovou barvou, zdrojovou barvou nebo vůbec ne.
Celkový počet kombinací je 2 (vlevo nahoře) krát 2 (vpravo dole) krát 3 (pro střed) nebo 12. Jedná se o 12 základních režimů kompozitování Porter-Duff.
Na konec kompozitingu digitálních obrázků (strana 256), Porter a Duff přidejte 13. režim s názvem plus (odpovídající členu SkiaSharp SKBlendMode.Plus
a světlejší režim W3C (který se nesmí zaměňovat s režimem W3C Lighten.) Tento Plus
režim přidá cílové a zdrojové barvy, což je proces, který se za chvíli podrobněji popisuje.
Skia přidá 14. režim, Modulate
který je velmi podobný s Plus
tím rozdílem, že cíl a zdrojové barvy jsou vynásobeny. Lze s ním zacházet jako s dalším režimem mixu Porter-Duff.
Tady je 14 režimů Porter-Duff definovaných ve SkiaSharpu. Tabulka ukazuje, jak v diagramu výše obarví všechny tři neprázdné oblasti:
Režim | Cíl | Průsečíku | Source |
---|---|---|---|
Clear |
|||
Src |
Zdroj | X | |
Dst |
X | Cíl | |
SrcOver |
X | Zdroj | X |
DstOver |
X | Cíl | X |
SrcIn |
Zdroj | ||
DstIn |
Cíl | ||
SrcOut |
X | ||
DstOut |
X | ||
SrcATop |
X | Zdroj | |
DstATop |
Cíl | X | |
Xor |
X | X | |
Plus |
X | Sum | X |
Modulate |
Produkt |
Tyto režimy blendu jsou symetrické. Zdroj a cíl je možné vyměnit a všechny režimy jsou stále dostupné.
Zásady vytváření názvů režimů se řídí několika jednoduchými pravidly:
- Src nebo Dst samotný znamená, že jsou viditelné pouze zdrojové nebo cílové pixely.
- Přípona Over označuje, co je viditelné v průsečíku. Zdroj nebo cíl je nakreslen "přes" druhý.
- Přípona In znamená, že barva pouze průniku. Výstup je omezen pouze na část zdroje nebo cíle, která je "in" druhá.
- Přípona Out znamená, že průsečík není barevný. Výstup je pouze část zdroje nebo cíle, která je mimo průnik.
- Přípona ATop je sjednocení in and Out. Zahrnuje oblast, kde je zdroj nebo cíl "atop" druhého.
Všimněte si rozdílu mezi režimy Plus
a Modulate
režimy. Tyto režimy provádějí jiný typ výpočtu na zdrojových a cílových pixelech. Stručně jsou popsány podrobněji.
Stránka Mřížka Porter-Duff zobrazuje všechny režimy na jedné obrazovce ve formě mřížky. Každý režim je samostatná instance SKCanvasView
. Z tohoto důvodu je třída odvozena z SKCanvasView
pojmenovaného PorterDuffCanvasView
. Statický konstruktor vytvoří dva rastrové obrázky stejné velikosti, jeden s hněděným obdélníkem v levé horní oblasti a druhý s bludným obdélníkem:
class PorterDuffCanvasView : SKCanvasView
{
static SKBitmap srcBitmap, dstBitmap;
static PorterDuffCanvasView()
{
dstBitmap = new SKBitmap(300, 300);
srcBitmap = new SKBitmap(300, 300);
using (SKPaint paint = new SKPaint())
{
using (SKCanvas canvas = new SKCanvas(dstBitmap))
{
canvas.Clear();
paint.Color = new SKColor(0xC0, 0x80, 0x00);
canvas.DrawRect(new SKRect(0, 0, 200, 200), paint);
}
using (SKCanvas canvas = new SKCanvas(srcBitmap))
{
canvas.Clear();
paint.Color = new SKColor(0x00, 0x80, 0xC0);
canvas.DrawRect(new SKRect(100, 100, 300, 300), paint);
}
}
}
···
}
Konstruktor instance má parametr typu SKBlendMode
. Uloží tento parametr do pole.
class PorterDuffCanvasView : SKCanvasView
{
···
SKBlendMode blendMode;
public PorterDuffCanvasView(SKBlendMode blendMode)
{
this.blendMode = blendMode;
}
protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Find largest square that fits
float rectSize = Math.Min(info.Width, info.Height);
float x = (info.Width - rectSize) / 2;
float y = (info.Height - rectSize) / 2;
SKRect rect = new SKRect(x, y, x + rectSize, y + rectSize);
// Draw destination bitmap
canvas.DrawBitmap(dstBitmap, rect);
// Draw source bitmap
using (SKPaint paint = new SKPaint())
{
paint.BlendMode = blendMode;
canvas.DrawBitmap(srcBitmap, rect, paint);
}
// Draw outline
using (SKPaint paint = new SKPaint())
{
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Black;
paint.StrokeWidth = 2;
rect.Inflate(-1, -1);
canvas.DrawRect(rect, paint);
}
}
}
Přepsání OnPaintSurface
nakreslí dva rastrové obrázky. První se nakreslí normálně:
canvas.DrawBitmap(dstBitmap, rect);
Druhá je nakreslena objektem SKPaint
, kde BlendMode
byla vlastnost nastavena na argument konstruktoru:
using (SKPaint paint = new SKPaint())
{
paint.BlendMode = blendMode;
canvas.DrawBitmap(srcBitmap, rect, paint);
}
Zbytek OnPaintSurface
přepsání nakreslí kolem rastrového obrázku obdélník, který označuje jejich velikosti.
Třída PorterDuffGridPage
vytvoří čtrnáct instancí PorterDurffCanvasView
, jeden pro každý člen blendModes
pole. Pořadí SKBlendModes
členů v poli se trochu liší od tabulky, aby bylo možné umístit podobné režimy vedle sebe. 14 instancí jsou uspořádány PorterDuffCanvasView
spolu s popisky v Grid
:
public class PorterDuffGridPage : ContentPage
{
public PorterDuffGridPage()
{
Title = "Porter-Duff Grid";
SKBlendMode[] blendModes =
{
SKBlendMode.Src, SKBlendMode.Dst, SKBlendMode.SrcOver, SKBlendMode.DstOver,
SKBlendMode.SrcIn, SKBlendMode.DstIn, SKBlendMode.SrcOut, SKBlendMode.DstOut,
SKBlendMode.SrcATop, SKBlendMode.DstATop, SKBlendMode.Xor, SKBlendMode.Plus,
SKBlendMode.Modulate, SKBlendMode.Clear
};
Grid grid = new Grid
{
Margin = new Thickness(5)
};
for (int row = 0; row < 4; row++)
{
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Star });
}
for (int col = 0; col < 3; col++)
{
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star });
}
for (int i = 0; i < blendModes.Length; i++)
{
SKBlendMode blendMode = blendModes[i];
int row = 2 * (i / 4);
int col = i % 4;
Label label = new Label
{
Text = blendMode.ToString(),
HorizontalTextAlignment = TextAlignment.Center
};
Grid.SetRow(label, row);
Grid.SetColumn(label, col);
grid.Children.Add(label);
PorterDuffCanvasView canvasView = new PorterDuffCanvasView(blendMode);
Grid.SetRow(canvasView, row + 1);
Grid.SetColumn(canvasView, col);
grid.Children.Add(canvasView);
}
Content = grid;
}
}
Tady je výsledek:
Budete se chtít přesvědčit, že transparentnost je zásadní pro správné fungování režimů mixu Porter-Duff. Třída PorterDuffCanvasView
obsahuje celkem tři volání metody Canvas.Clear
. Všichni používají bezparametrovou metodu, která nastaví všechny pixely na transparentní:
canvas.Clear();
Zkuste některé z těchto volání změnit tak, aby pixely byly nastaveny na neprůhlenou bílou:
canvas.Clear(SKColors.White);
Po této změně se některé režimy směsi zdají fungovat, ale jiné nebudou fungovat. Pokud nastavíte pozadí zdrojového rastrového obrázku na bílou, režim nefunguje, SrcOver
protože ve zdrojovém rastrovém obrázku nejsou žádné průhledné pixely, aby se cíl zobrazil. Pokud nastavíte pozadí cílového rastrového obrázku nebo plátna na bílou, nebude fungovat, DstOver
protože cíl nemá žádné průhledné pixely.
Může být lákavé nahradit rastrové obrázky na stránce Mřížka Porter-Duff jednoduššími DrawRect
voláními. To bude fungovat pro cílový obdélník, ale ne pro zdrojový obdélník. Zdrojový obdélník musí zahrnovat více než jen modrobarevnou oblast. Zdrojový obdélník musí obsahovat průhlednou oblast, která odpovídá barevné oblasti cíle. Tyto režimy prolnutí budou fungovat pouze tehdy.
Použití matek s Porter-Duffem
Stránka s kompozitováním z cihlové zdi ukazuje příklad klasického kompozitujícího úkolu: Obrázek musí být sestaven z několika částí, včetně rastrového obrázku s pozadím, které je potřeba odstranit. Tady je rastrový obrázek SeatedMonkey.jpg s problematickým pozadím:
Při přípravě na kompozitování byl vytvořen odpovídající matný obrázek, což je další rastrový obrázek, který je černý, kde chcete, aby se obrázek zobrazoval a průhledný jinak. Tento soubor má název SeatedMonkeyMatte.png a patří mezi prostředky ve složce Media v ukázce SkiaSharpFormsDemos:
Nejedná se o odborně vytvořenou matnou matici. V optimálním případě by matný materiál měl obsahovat částečně průhledné pixely kolem okraje černých pixelů a tato matná matice ne.
Soubor XAML pro stránku s cihlovou stěnou vytvoří SKCanvasView
instanci a vytvoří Button
instanci uživatele procesem vytvoření konečného obrázku:
<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.BrickWallCompositingPage"
Title="Brick-Wall Compositing">
<StackLayout>
<skia:SKCanvasView x:Name="canvasView"
VerticalOptions="FillAndExpand"
PaintSurface="OnCanvasViewPaintSurface" />
<Button Text="Show sitting monkey"
HorizontalOptions="Center"
Margin="0, 10"
Clicked="OnButtonClicked" />
</StackLayout>
</ContentPage>
Soubor s kódem načte dva rastrové obrázky, které potřebuje, a zpracuje Clicked
událost objektu Button
. Pro každé Button
kliknutí se step
pole zvýší a pro vlastnost je nastavena Button
nová Text
vlastnost . Když step
dosáhne hodnoty 5, nastaví se zpět na hodnotu 0:
public partial class BrickWallCompositingPage : ContentPage
{
SKBitmap monkeyBitmap = BitmapExtensions.LoadBitmapResource(
typeof(BrickWallCompositingPage),
"SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");
SKBitmap matteBitmap = BitmapExtensions.LoadBitmapResource(
typeof(BrickWallCompositingPage),
"SkiaSharpFormsDemos.Media.SeatedMonkeyMatte.png");
int step = 0;
public BrickWallCompositingPage ()
{
InitializeComponent ();
}
void OnButtonClicked(object sender, EventArgs args)
{
Button btn = (Button)sender;
step = (step + 1) % 5;
switch (step)
{
case 0: btn.Text = "Show sitting monkey"; break;
case 1: btn.Text = "Draw matte with DstIn"; break;
case 2: btn.Text = "Draw sidewalk with DstOver"; break;
case 3: btn.Text = "Draw brick wall with DstOver"; break;
case 4: btn.Text = "Reset"; break;
}
canvasView.InvalidateSurface();
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
···
}
}
Při prvním spuštění programu se nic nezobrazuje s výjimkou Button
:
Button
Stisknutím jednou se step
zvýší na 1 a obslužná rutina PaintSurface
se teď zobrazí SeatedMonkey.jpg:
public partial class BrickWallCompositingPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
···
float x = (info.Width - monkeyBitmap.Width) / 2;
float y = info.Height - monkeyBitmap.Height;
// Draw monkey bitmap
if (step >= 1)
{
canvas.DrawBitmap(monkeyBitmap, x, y);
}
···
}
}
Neexistuje žádný SKPaint
objekt, a proto žádný režim blendu. Rastrový obrázek se zobrazí v dolní části obrazovky:
Button
Stiskněte tlačítko znovu a step
navýší na 2. Toto je zásadní krok zobrazení souboru SeatedMonkeyMatte.png :
public partial class BrickWallCompositingPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
···
// Draw matte to exclude monkey's surroundings
if (step >= 2)
{
using (SKPaint paint = new SKPaint())
{
paint.BlendMode = SKBlendMode.DstIn;
canvas.DrawBitmap(matteBitmap, x, y, paint);
}
}
···
}
}
Režim mixu znamená SKBlendMode.DstIn
, že cíl se zachová v oblastech odpovídajících neprůhledým oblastem zdroje. Zbytek cílového obdélníku odpovídající původnímu rastru se stane průhledný:
Pozadí bylo odebráno.
Dalším krokem je nakreslit obdélník, který se podobá chodníku, na který opice sedí. Vzhled tohoto chodníku je založen na složení dvou shaderů: solid color shader a perlin šum shader:
public partial class BrickWallCompositingPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
···
const float sidewalkHeight = 80;
SKRect rect = new SKRect(info.Rect.Left, info.Rect.Bottom - sidewalkHeight,
info.Rect.Right, info.Rect.Bottom);
// Draw gravel sidewalk for monkey to sit on
if (step >= 3)
{
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateCompose(
SKShader.CreateColor(SKColors.SandyBrown),
SKShader.CreatePerlinNoiseTurbulence(0.1f, 0.3f, 1, 9));
paint.BlendMode = SKBlendMode.DstOver;
canvas.DrawRect(rect, paint);
}
}
···
}
}
Protože tento chodník musí jít za opicí, režim mixu je DstOver
. Cíl se zobrazí jenom tam, kde je pozadí průhledné:
Posledním krokem je přidání cihlové zdi. Program používá brick-wall bitmap dlaždice k dispozici jako static vlastnost BrickWallTile
ve AlgorithmicBrickWallPage
třídě. Do volání pro přesunutí dlaždic se přidá SKShader.CreateBitmap
transformace překladu tak, aby dolní řádek byl celou dlaždicí:
public partial class BrickWallCompositingPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
···
// Draw bitmap tiled brick wall behind monkey
if (step >= 4)
{
using (SKPaint paint = new SKPaint())
{
SKBitmap bitmap = AlgorithmicBrickWallPage.BrickWallTile;
float yAdjust = (info.Height - sidewalkHeight) % bitmap.Height;
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat,
SKMatrix.MakeTranslation(0, yAdjust));
paint.BlendMode = SKBlendMode.DstOver;
canvas.DrawRect(info.Rect, paint);
}
}
}
}
Pro usnadnění volání DrawRect
zobrazí tento shader na celé plátno, ale DstOver
režim omezuje výstup pouze na oblast plátna, která je stále průhledná:
Je zřejmé, že existují další způsoby, jak vytvořit tuto scénu. Mohlo by se vytvořit od pozadí a přejít na popředí. Použití režimů mixu vám ale dává větší flexibilitu. Zejména použití matného objektu umožňuje vyloučit pozadí rastrového obrázku z složené scény.
Jak jste se dozvěděli v článku Výřez s cestami a oblastmi, SKCanvas
třída definuje tři typy výřezů, které odpovídají ClipRect
, ClipPath
a ClipRegion
metodám. Režimy blendu Porter-Duff přidávají další typ výřezu, který umožňuje omezit obrázek na cokoli, co můžete kreslit, včetně rastrových obrázků. Matná matice použitá ve zděných kompozitingech v podstatě definuje oblast výřezu.
Průhlednost přechodu a přechody
Příklady režimů mixu Porter-Duff zobrazené dříve v tomto článku obsahují všechny zahrnuté obrázky, které se skládají z neprůhledných pixelů a průhledných pixelů, ale ne částečně průhledných pixelů. Pro tyto pixely jsou definovány také funkce režimu blendu. Následující tabulka představuje formální definici režimů mixu Porter-Duff, která používá notaci nalezenou v odkazu Skia SkBlendMode. (Protože Odkaz SkBlendMode je odkaz skia, používá se syntaxe jazyka C++.)
Koncepčně jsou červené, zelené, modré a alfa součásti každého pixelu převedeny z bajtů na čísla s plovoucí desetinou čárkou v rozsahu od 0 do 1. Pro alfa kanál je 0 plně transparentní a 1 je plně neprůhledný.
Zápis v následující tabulce používá následující zkratky:
- Da je cílový alfa kanál.
- Dc je cílová barva RGB.
- Sa je zdrojový alfa kanál.
- Sc je zdrojová barva RGB.
Barvy RGB jsou předem vynásobeny alfa hodnotou. Pokud například Sc představuje čistě červenou, ale Sa je 0x80, barva RGB je (0x80, 0, 0). Pokud je Sa 0, všechny komponenty RGB jsou také nula.
Výsledek se zobrazí v závorkách s alfa kanálem a barvou RGB oddělenou čárkou: [alfa, barva]. Pro barvu se výpočet provádí samostatně pro červené, zelené a modré komponenty:
Režim | Operace |
---|---|
Clear |
[0, 0] |
Src |
[Sa, Sc] |
Dst |
[Da, Dc] |
SrcOver |
[Sa + Da· (1 – Sa), Sc + Dc· (1 – Sa) |
DstOver |
[Da+ Sa· (1 – Da), Dc + Sc· (1 – Da) |
SrcIn |
[Sa· Da, Sc· Da] |
DstIn |
[Da· Sa, Dc·Sa] |
SrcOut |
[Sa· (1 – Da), Sc· (1 – Da)] |
DstOut |
[Da· (1 – Sa), Dc· (1 – Sa)] |
SrcATop |
[Da, Sc· Da + Dc· (1 – Sa)] |
DstATop |
[Sa, Dc·Sa + Sc· (1 – Da)] |
Xor |
[Sa + Da – 2· Sa· Da, Sc· (1 – Da) + Dc· (1 – Sa)] |
Plus |
[Sa + Da, Sc + Dc] |
Modulate |
[Sa· Da, Sc· Dc] |
Tyto operace se snadněji analyzují, když jsou da a Sa buď 0, nebo 1. Například pro výchozí SrcOver
režim, pokud Sa je 0, pak Sc je také 0 a výsledek je [Da, Dc], cíl alfa a barva. Pokud je Sa 1, výsledek je [Sa, Sc], zdroj alfa a barva nebo [1, Sc].
Režimy Plus
jsou Modulate
trochu jiné než ostatní v tom, že nové barvy můžou mít za následek kombinaci zdroje a cíle. Režim Plus
lze interpretovat buď s komponentami bajtů, nebo komponentami s plovoucí desetinou čárkou. Na stránce mřížky Porter-Duff zobrazené výše je cílová barva (0xC0, 0x80, 0x00) a zdrojová barva je (0x00, 0x80, 0xC0). Každá dvojice součástí je přidána, ale součet je uchycený na 0xFF. Výsledkem je barva (0xC0, 0xFF, 0xC0).) To je barva zobrazená v průsečíku.
Modulate
V režimu musí být hodnoty RGB převedeny na plovoucí desetina. Cílová barva je (0,75, 0,5, 0) a zdroj je (0, 0,5, 0,75). Komponenty RGB se vynásobí společně a výsledek je (0, 0,25, 0). To je barva zobrazená v průsečíku na stránce mřížky Porter-Duff pro tento režim.
Stránka Průhlednost porter-Duff umožňuje prozkoumat, jak režimy mixu Porter-Duff fungují s grafickými objekty, které jsou částečně průhledné. Soubor XAML obsahuje Picker
režimy Porter-Duff:
<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.PorterDuffTransparencyPage"
Title="Porter-Duff Transparency">
<StackLayout>
<skiaviews:SKCanvasView x:Name="canvasView"
VerticalOptions="FillAndExpand"
PaintSurface="OnCanvasViewPaintSurface" />
<Picker x:Name="blendModePicker"
Title="Blend Mode"
Margin="10"
SelectedIndexChanged="OnPickerSelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type skia:SKBlendMode}">
<x:Static Member="skia:SKBlendMode.Clear" />
<x:Static Member="skia:SKBlendMode.Src" />
<x:Static Member="skia:SKBlendMode.Dst" />
<x:Static Member="skia:SKBlendMode.SrcOver" />
<x:Static Member="skia:SKBlendMode.DstOver" />
<x:Static Member="skia:SKBlendMode.SrcIn" />
<x:Static Member="skia:SKBlendMode.DstIn" />
<x:Static Member="skia:SKBlendMode.SrcOut" />
<x:Static Member="skia:SKBlendMode.DstOut" />
<x:Static Member="skia:SKBlendMode.SrcATop" />
<x:Static Member="skia:SKBlendMode.DstATop" />
<x:Static Member="skia:SKBlendMode.Xor" />
<x:Static Member="skia:SKBlendMode.Plus" />
<x:Static Member="skia:SKBlendMode.Modulate" />
</x:Array>
</Picker.ItemsSource>
<Picker.SelectedIndex>
3
</Picker.SelectedIndex>
</Picker>
</StackLayout>
</ContentPage>
Soubor kódu zaplní dva obdélníky stejné velikosti pomocí lineárního přechodu. Cílový přechod je z pravého horního rohu do levého dolního rohu. Je v pravém horním rohu zhnědlý, ale pak směrem ke středu začíná ulétnout na průhledný a je průhledný v levém dolním rohu.
Zdrojový obdélník má přechod z levého horního rohu do pravého dolního rohu. Levý horní roh je bludný, ale zase zmizí na průhledný a je průhledný v pravém dolním rohu.
public partial class PorterDuffTransparencyPage : ContentPage
{
public PorterDuffTransparencyPage()
{
InitializeComponent();
}
void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
canvasView.InvalidateSurface();
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Make square display rectangle smaller than canvas
float size = 0.9f * Math.Min(info.Width, info.Height);
float x = (info.Width - size) / 2;
float y = (info.Height - size) / 2;
SKRect rect = new SKRect(x, y, x + size, y + size);
using (SKPaint paint = new SKPaint())
{
// Draw destination
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(rect.Right, rect.Top),
new SKPoint(rect.Left, rect.Bottom),
new SKColor[] { new SKColor(0xC0, 0x80, 0x00),
new SKColor(0xC0, 0x80, 0x00, 0) },
new float[] { 0.4f, 0.6f },
SKShaderTileMode.Clamp);
canvas.DrawRect(rect, paint);
// Draw source
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(rect.Left, rect.Top),
new SKPoint(rect.Right, rect.Bottom),
new SKColor[] { new SKColor(0x00, 0x80, 0xC0),
new SKColor(0x00, 0x80, 0xC0, 0) },
new float[] { 0.4f, 0.6f },
SKShaderTileMode.Clamp);
// Get the blend mode from the picker
paint.BlendMode = blendModePicker.SelectedIndex == -1 ? 0 :
(SKBlendMode)blendModePicker.SelectedItem;
canvas.DrawRect(rect, paint);
// Stroke surrounding rectangle
paint.Shader = null;
paint.BlendMode = SKBlendMode.SrcOver;
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Black;
paint.StrokeWidth = 3;
canvas.DrawRect(rect, paint);
}
}
}
Tento program ukazuje, že režimy mixu Porter-Duff lze použít s grafickými objekty jinými než rastry. Zdroj však musí obsahovat průhlednou oblast. Toto je tento případ, protože přechod vyplní obdélník, ale část přechodu je průhledná.
Tady jsou tři příklady:
Konfigurace cíle a zdroje je velmi podobná diagramům zobrazeným na stránce 255 původního papíru Porter-Duff Compositing Digital Images , ale tato stránka ukazuje, že režimy směsi se dobře chovají pro oblasti částečné průhlednosti.
Pro některé různé efekty můžete použít průhledné přechody. Jednou z možností je maskování, což je podobné technice znázorněné v kruhových přechodech na stránce s kruhovými přechody SkiaSharp. Většina stránky Maska pro kompoziting je podobná dřívějšímu programu. Načte rastrový zdroj a určuje obdélník, ve kterém se má zobrazit. Paprskový přechod se vytvoří na základě předem určeného středu a poloměru:
public class CompositingMaskPage : ContentPage
{
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
typeof(CompositingMaskPage),
"SkiaSharpFormsDemos.Media.MountainClimbers.jpg");
static readonly SKPoint CENTER = new SKPoint(180, 300);
static readonly float RADIUS = 120;
public CompositingMaskPage ()
{
Title = "Compositing Mask";
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();
// Find rectangle to display bitmap
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 in rectangle
canvas.DrawBitmap(bitmap, rect);
// Adjust center and radius for scaled and offset bitmap
SKPoint center = new SKPoint(scale * CENTER.X + x,
scale * CENTER.Y + y);
float radius = scale * RADIUS;
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateRadialGradient(
center,
radius,
new SKColor[] { SKColors.Black,
SKColors.Transparent },
new float[] { 0.6f, 1 },
SKShaderTileMode.Clamp);
paint.BlendMode = SKBlendMode.DstIn;
// Display rectangle using that gradient and blend mode
canvas.DrawRect(rect, paint);
}
canvas.DrawColor(SKColors.Pink, SKBlendMode.DstOver);
}
}
Rozdíl v tomto programu spočívá v tom, že přechod začíná černou ve středu a končí průhledností. Zobrazí se na rastrovém obrázku s režimem DstIn
prolnutí , který zobrazuje cíl pouze v oblastech zdroje, které nejsou transparentní.
DrawRect
Po volání je celý povrch plátna průhledný s výjimkou kruhu definovaného paprskovým přechodem. Provede se konečný hovor:
canvas.DrawColor(SKColors.Pink, SKBlendMode.DstOver);
Všechny průhledné oblasti plátna jsou barevně růžové:
Můžete také použít režimy Porter-Duff a částečně průhledné přechody pro přechody z jednoho obrázku do druhého. Stránka Přechody přechodu obsahuje Slider
popis úrovně průběhu přechodu od 0 do 1 a Picker
k výběru požadovaného typu přechodu:
<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.GradientTransitionsPage"
Title="Gradient Transitions">
<StackLayout>
<skia:SKCanvasView x:Name="canvasView"
VerticalOptions="FillAndExpand"
PaintSurface="OnCanvasViewPaintSurface" />
<Slider x:Name="progressSlider"
Margin="10, 0"
ValueChanged="OnSliderValueChanged" />
<Label Text="{Binding Source={x:Reference progressSlider},
Path=Value,
StringFormat='Progress = {0:F2}'}"
HorizontalTextAlignment="Center" />
<Picker x:Name="transitionPicker"
Title="Transition"
Margin="10"
SelectedIndexChanged="OnPickerSelectedIndexChanged" />
</StackLayout>
</ContentPage>
Soubor s kódem načte dva rastrové prostředky, které předvádějí přechod. Jedná se o stejné dva obrázky, které se používají na stránce Rozpustit rastrové obrázky dříve v tomto článku. Kód také definuje výčet se třemi členy odpovídajícími třem typům přechodů – lineární, radiální a uklidit. Tyto hodnoty jsou načteny do Picker
:
public partial class GradientTransitionsPage : ContentPage
{
SKBitmap bitmap1 = BitmapExtensions.LoadBitmapResource(
typeof(GradientTransitionsPage),
"SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");
SKBitmap bitmap2 = BitmapExtensions.LoadBitmapResource(
typeof(GradientTransitionsPage),
"SkiaSharpFormsDemos.Media.FacePalm.jpg");
enum TransitionMode
{
Linear,
Radial,
Sweep
};
public GradientTransitionsPage ()
{
InitializeComponent ();
foreach (TransitionMode mode in Enum.GetValues(typeof(TransitionMode)))
{
transitionPicker.Items.Add(mode.ToString());
}
transitionPicker.SelectedIndex = 0;
}
void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
canvasView.InvalidateSurface();
}
void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
canvasView.InvalidateSurface();
}
···
}
Soubor kódu za kódem vytvoří tři SKPaint
objekty. Objekt paint0
nepoužívá režim blendu. Tento objekt malování slouží k vykreslení obdélníku s přechodem, který jde z černé na průhledný, jak je uvedeno v colors
poli. Pole positions
je založeno na pozici Slider
, ale poněkud upraveno. Pokud je hodnota Slider
minimální nebo maximální, progress
hodnoty jsou 0 nebo 1 a jedna z těchto dvou rastrových obrázků by měla být plně viditelná. Pole positions
musí být pro tyto hodnoty nastaveno odpovídajícím způsobem.
progress
Pokud je hodnota 0, positions
matice obsahuje hodnoty -0,1 a 0. SkiaSharp upraví první hodnotu tak, aby byla rovna 0, což znamená, že přechod je černý pouze při 0 a průhledné jinak. Pokud progress
je 0,5, matice obsahuje hodnoty 0,45 a 0,55. Přechod je černý od 0 do 0,45, pak přejde na průhledný a je zcela průhledný od 0,55 do 1. Když progress
je 1, positions
matice je 1 a 1,1, což znamená, že přechod je černý od 0 do 1.
position
Obě pole colors
se používají ve třech metodáchSKShader
, které vytvářejí přechod. Na základě Picker
výběru se vytvoří pouze jeden z těchto shaderů:
public partial class GradientTransitionsPage : ContentPage
{
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Assume both bitmaps are square for display rectangle
float size = Math.Min(info.Width, info.Height);
SKRect rect = SKRect.Create(size, size);
float x = (info.Width - size) / 2;
float y = (info.Height - size) / 2;
rect.Offset(x, y);
using (SKPaint paint0 = new SKPaint())
using (SKPaint paint1 = new SKPaint())
using (SKPaint paint2 = new SKPaint())
{
SKColor[] colors = new SKColor[] { SKColors.Black,
SKColors.Transparent };
float progress = (float)progressSlider.Value;
float[] positions = new float[]{ 1.1f * progress - 0.1f,
1.1f * progress };
switch ((TransitionMode)transitionPicker.SelectedIndex)
{
case TransitionMode.Linear:
paint0.Shader = SKShader.CreateLinearGradient(
new SKPoint(rect.Left, 0),
new SKPoint(rect.Right, 0),
colors,
positions,
SKShaderTileMode.Clamp);
break;
case TransitionMode.Radial:
paint0.Shader = SKShader.CreateRadialGradient(
new SKPoint(rect.MidX, rect.MidY),
(float)Math.Sqrt(Math.Pow(rect.Width / 2, 2) +
Math.Pow(rect.Height / 2, 2)),
colors,
positions,
SKShaderTileMode.Clamp);
break;
case TransitionMode.Sweep:
paint0.Shader = SKShader.CreateSweepGradient(
new SKPoint(rect.MidX, rect.MidY),
colors,
positions);
break;
}
canvas.DrawRect(rect, paint0);
paint1.BlendMode = SKBlendMode.SrcOut;
canvas.DrawBitmap(bitmap1, rect, paint1);
paint2.BlendMode = SKBlendMode.DstOver;
canvas.DrawBitmap(bitmap2, rect, paint2);
}
}
}
Tento přechod se zobrazí v obdélníku bez režimu prolnutí. Po tomto DrawRect
volání plátno jednoduše obsahuje přechod z černé na průhledný. Množství černé hodnoty se zvyšuje s vyššími Slider
hodnotami.
V posledních čtyřech příkazech obslužné rutiny PaintSurface
se zobrazí dvě rastrové obrázky. Režim SrcOut
blendu znamená, že první rastrový obrázek se zobrazí pouze v průhledných oblastech pozadí. Režim DstOver
druhého rastrového obrázku znamená, že se druhý rastrový obrázek zobrazí pouze v oblastech, kde se první rastrový obrázek nezobrazuje.
Následující snímky obrazovky znázorňují tři různé typy přechodů, z nichž každý má 50 % značku: