Transformace měřítka
Objevte transformaci škálování SkiaSharp pro škálování objektů na různé velikosti.
Jak jste viděli v článku Přeložit transformaci , může transformace přeložit grafický objekt z jednoho umístění do jiného. Naproti tomu transformace měřítka změní velikost grafického objektu:
Transformace měřítka také často způsobuje, že se grafické souřadnice přesunou, protože se zvětší.
Dříve jste viděli dva transformační vzorce, které popisují účinky faktorů překladu a dx
dy
:
x' = x + dx
y' = y + dy
Faktory měřítka sx
a sy
jsou násobené místo sčítání:
x' = sx · X
y' = sy · Y
Výchozí hodnoty přeložených faktorů jsou 0; výchozí hodnoty faktorů škálování jsou 1.
Třída SKCanvas
definuje čtyři Scale
metody. První Scale
metoda je pro případy, kdy chcete stejný horizontální a svislý faktor škálování:
public void Scale (Single s)
To se označuje jako isotropní škálování – škálování, které je stejné v obou směrech. Isotropní měřítko zachovává poměr stran objektu.
Druhá Scale
metoda umožňuje zadat různé hodnoty pro horizontální a vertikální škálování:
public void Scale (Single sx, Single sy)
Výsledkem je anisotropní škálování.
Třetí Scale
metoda kombinuje dva faktory škálování v jedné SKPoint
hodnotě:
public void Scale (SKPoint size)
Čtvrtá Scale
metoda bude stručně popsána.
Stránka Základní měřítko ukazuje metodu Scale
. Soubor BasicScalePage.xaml obsahuje dva Slider
prvky, které umožňují vybrat vodorovné a svislé faktory škálování mezi 0 a 10. Soubor BasicScalePage.xaml.cs kódu používá tyto hodnoty k volání Scale
před zobrazením zaobleného obdélníku s přerušovanou čárou a velikostí tak, aby se do levého horního rohu plátna vešel nějaký text:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.SkyBlue);
using (SKPaint strokePaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Red,
StrokeWidth = 3,
PathEffect = SKPathEffect.CreateDash(new float[] { 7, 7 }, 0)
})
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextSize = 50
})
{
canvas.Scale((float)xScaleSlider.Value,
(float)yScaleSlider.Value);
SKRect textBounds = new SKRect();
textPaint.MeasureText(Title, ref textBounds);
float margin = 10;
SKRect borderRect = SKRect.Create(new SKPoint(margin, margin), textBounds.Size);
canvas.DrawRoundRect(borderRect, 20, 20, strokePaint);
canvas.DrawText(Title, margin, -textBounds.Top + margin, textPaint);
}
}
Možná vás zajímá: Jak faktory škálování ovlivňují hodnotu vrácenou metodou MeasureText
SKPaint
? Odpověď je: Vůbec ne. Scale
je metoda SKCanvas
. Nemá vliv na nic, co s objektem SKPaint
děláte, dokud ho nepoužijete k vykreslení na plátně.
Jak vidíte, vše, co se vykresluje po Scale
volání, se proporcionálně zvýší:
Text, šířka přerušované čáry, délka pomlček v této čáře, zaokrouhlování rohů a 10 pixelů okraje mezi levým a horním okrajem plátna a zaoblený obdélník se řídí stejnými faktory měřítka.
Důležité
Univerzální platforma Windows nevykresluje správně anisotropně škálovaný text.
Anisotropní měřítko způsobí, že šířka tahu se pro čáry zarovnané s vodorovnou a svislou osou liší. (To je také zřejmé z prvního obrázku na této stránce.) Pokud nechcete, aby šířka tahu byla ovlivněna faktory měřítka, nastavte ji na 0 a vždy bude mít šířku o jeden pixel bez Scale
ohledu na nastavení.
Měřítko je relativní vzhledem k levému hornímu rohu plátna. To může být přesně to, co chcete, ale nemusí to být. Předpokládejme, že chcete umístit text a obdélník někam jinam na plátno a chcete ho škálovat vzhledem ke středu. V takovém případě můžete použít čtvrtou verzi Scale
metody, která obsahuje dva další parametry pro určení středu škálování:
public void Scale (Single sx, Single sy, Single px, Single py)
Parametry px
definují bod, který se někdy označuje jako centrum škálování, ale v dokumentaci SkiaSharp se označuje jako kontingenční bod.py
Jedná se o bod relativní vzhledem k levému hornímu rohu plátna, které není ovlivněno škálováním. K veškerému škálování dochází vzhledem k tomuto středu.
Na stránce na středovém měřítku se dozvíte, jak to funguje. Obslužná PaintSurface
rutina se podobá programu Základní měřítko s tím rozdílem, že margin
hodnota je vypočítána tak, aby text vodorovně zarovná do středu, což znamená, že program funguje nejlépe v režimu na výšku:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.SkyBlue);
using (SKPaint strokePaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Red,
StrokeWidth = 3,
PathEffect = SKPathEffect.CreateDash(new float[] { 7, 7 }, 0)
})
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextSize = 50
})
{
SKRect textBounds = new SKRect();
textPaint.MeasureText(Title, ref textBounds);
float margin = (info.Width - textBounds.Width) / 2;
float sx = (float)xScaleSlider.Value;
float sy = (float)yScaleSlider.Value;
float px = margin + textBounds.Width / 2;
float py = margin + textBounds.Height / 2;
canvas.Scale(sx, sy, px, py);
SKRect borderRect = SKRect.Create(new SKPoint(margin, margin), textBounds.Size);
canvas.DrawRoundRect(borderRect, 20, 20, strokePaint);
canvas.DrawText(Title, margin, -textBounds.Top + margin, textPaint);
}
}
Levý horní roh zaobleného obdélníku je umístěn margin
pixely zleva od plátna a margin
pixelů od horní části. Poslední dva argumenty metody Scale
jsou nastaveny na tyto hodnoty a šířku a výšku textu, což je také šířka a výška zaobleného obdélníku. To znamená, že veškeré škálování je relativní vzhledem ke středu tohoto obdélníku:
Prvky Slider
v tomto programu mají rozsah –10 až 10. Jak vidíte, záporné hodnoty vertikálního měřítka (například na obrazovce Androidu uprostřed) způsobují, že objekty se překlopí kolem vodorovné osy, která prochází středem měřítka. Záporné hodnoty horizontálního škálování (například na obrazovce UPW vpravo) způsobují, že objekty se překlopí kolem svislé osy, která prochází středem měřítka.
Verze Scale
metody s kontingenčními body je zkratka pro řadu tří Translate
volání a Scale
volání. Možná budete chtít zjistit, jak to funguje, nahrazením Scale
metody na stránce Centered Scale následujícím kódem:
canvas.Translate(-px, -py);
Jedná se o záporné hodnoty souřadnic bodů kontingenčního bodu.
Teď program spusťte znovu. Uvidíte, že obdélník a text jsou posunuty tak, aby střed byl v levém horním rohu plátna. Sotva ji uvidíš. Posuvníky samozřejmě nefungují, protože teď se program vůbec nes škáluje.
Teď před tímto Translate
voláním přidejte základní Scale
volání (bez centra škálování):
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);
Pokud jste obeznámeni s tímto cvičením v jiných grafických programovacích systémech, můžete si myslet, že je to špatně, ale není to. Skia zpracovává následné transformace volání trochu jinak než to, co byste mohli znát.
Při následných Scale
voláních Translate
je střed zaobleného obdélníku stále v levém horním rohu, ale teď ho můžete škálovat vzhledem k levému hornímu rohu plátna, což je také střed zaobleného obdélníku.
Teď před tímto Scale
voláním přidejte další Translate
volání se středními hodnotami:
canvas.Translate(px, py);
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);
Tím se škálovaný výsledek přesune zpět na původní pozici. Tato tři volání jsou ekvivalentní těmto třem voláním:
canvas.Scale(sx, sy, px, py);
Jednotlivé transformace jsou složené tak, aby vzorec celkové transformace byl:
x' = sx · (x – px) + px
y' = sy · (y – py) + py
Mějte na paměti, že výchozí hodnoty sx
a sy
jsou 1. Je snadné přesvědčit se, že kontingenční bod (px, py) není transformován těmito vzorci. Zůstane ve stejném umístění vzhledem k plátnu.
Při kombinování Translate
a Scale
volání záleží na objednávce. Translate
Pokud nastane následujícíScale
, faktory překladu se efektivně škálují faktory škálování. Translate
Pokud je před ním Scale
, faktory překladu nejsou škálovány. Tento proces se stává poněkud jasnější (i když více matematický) při zavedení předmětu transformovaných matic.
Třída SKPath
definuje vlastnost jen Bounds
pro čtení, která vrací SKRect
definici rozsahu souřadnic v cestě. Pokud se například Bounds
vlastnost získá z dříve vytvořené cesty hendecagramu, Left
jsou vlastnosti Top
obdélníku přibližně –100, Right
a Bottom
vlastnosti jsou přibližně 100 a Width
Height
vlastnosti jsou přibližně 200. (Většina skutečných hodnot je o něco menší, protože body hvězd jsou definovány kruhem s poloměrem 100, ale pouze horní bod je rovnoběžný s vodorovnou nebo svislou osou.)
Dostupnost těchto informací znamená, že by mělo být možné odvodit měřítko a přeložit faktory vhodné pro škálování cesty k velikosti plátna. Stránka Anisotropní měřítko ukazuje, že s 11-špičatou hvězdou. Anisotropní měřítko znamená, že je nerovný ve vodorovných a svislých směrech, což znamená, že hvězda si nezachová svůj původní poměr stran. Tady je příslušný kód v obslužné rutině PaintSurface
:
SKPath path = HendecagramPage.HendecagramPath;
SKRect pathBounds = path.Bounds;
using (SKPaint fillPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Pink
})
using (SKPaint strokePaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Blue,
StrokeWidth = 3,
StrokeJoin = SKStrokeJoin.Round
})
{
canvas.Scale(info.Width / pathBounds.Width,
info.Height / pathBounds.Height);
canvas.Translate(-pathBounds.Left, -pathBounds.Top);
canvas.DrawPath(path, fillPaint);
canvas.DrawPath(path, strokePaint);
}
Obdélník pathBounds
se získá v horní části tohoto kódu a později se použije s šířkou a výškou plátna ve Scale
volání. Toto volání samo o sobě zvětšuje souřadnice cesty, když se vykreslí voláním DrawPath
, ale hvězda se zacentruje v pravém horním rohu plátna. Musí být posunut dolů a doleva. Toto je úloha Translate
hovoru. Tyto dvě vlastnosti pathBounds
jsou přibližně –100, takže faktory překladu jsou přibližně 100. Translate
Vzhledem k tomu, že volání je po Scale
volání, tyto hodnoty se efektivně škálují faktory škálování, takže přesouvají střed hvězdy na střed plátna:
Dalším způsobem, jak můžete uvažovat o Scale
volání a Translate
volání, je určit účinek v obráceném pořadí: Translate
Volání posune cestu, takže se stane plně viditelným, ale orientovaným v levém horním rohu plátna. Metoda Scale
pak zvětší tuto hvězdičku vzhledem k levému hornímu rohu.
Ve skutečnosti se zdá, že hvězda je o něco větší než plátno. Problém je šířka tahu. Vlastnost Bounds
SKPath
označuje rozměry souřadnic kódovaných v cestě a to je to, co program používá ke škálování. Při vykreslení cesty s konkrétní šířkou tahu je vykreslená cesta větší než plátno.
Pokud chcete tento problém vyřešit, musíte tento problém kompenzovat. Jedním ze snadných přístupů v tomto programu je přidat následující příkaz přímo před Scale
volání:
pathBounds.Inflate(strokePaint.StrokeWidth / 2,
strokePaint.StrokeWidth / 2);
Tím se pathBounds
na všech čtyřech stranách zvětší obdélník o 1,5 jednotek. Toto je rozumné řešení pouze v případě, že je spojení tahu zaokrouhleno. Spojení miter může být delší a je obtížné vypočítat.
Můžete také použít podobnou techniku s textem, jak ukazuje anisotropní text stránky. Tady je relevantní část PaintSurface
obslužné rutiny z AnisotropicTextPage
třídy:
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Blue,
StrokeWidth = 0.1f,
StrokeJoin = SKStrokeJoin.Round
})
{
SKRect textBounds = new SKRect();
textPaint.MeasureText("HELLO", ref textBounds);
// Inflate bounds by the stroke width
textBounds.Inflate(textPaint.StrokeWidth / 2,
textPaint.StrokeWidth / 2);
canvas.Scale(info.Width / textBounds.Width,
info.Height / textBounds.Height);
canvas.Translate(-textBounds.Left, -textBounds.Top);
canvas.DrawText("HELLO", 0, 0, textPaint);
}
Je to podobná logika a text se rozbalí na velikost stránky na základě obdélníku vázaného na text vrácený ( MeasureText
což je o něco větší než skutečný text):
Pokud potřebujete zachovat poměr stran grafických objektů, budete chtít použít isotropní měřítko. Isotropní měřítko stránky ukazuje to pro 11-scípé hvězdy. Koncepčně jsou kroky pro zobrazení grafického objektu ve středu stránky s isotropní měřítko:
- Přeloží střed grafického objektu do levého horního rohu.
- Škálujte objekt na základě minimálního vodorovného a svislého rozměru stránky děleného rozměry grafického objektu.
- Přeložte střed škálovaného objektu na střed stránky.
Před IsotropicScalingPage
zobrazením hvězdičky provede tyto kroky v opačném pořadí:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKPath path = HendecagramArrayPage.HendecagramPath;
SKRect pathBounds = path.Bounds;
using (SKPaint fillPaint = new SKPaint())
{
fillPaint.Style = SKPaintStyle.Fill;
float scale = Math.Min(info.Width / pathBounds.Width,
info.Height / pathBounds.Height);
for (int i = 0; i <= 10; i++)
{
fillPaint.Color = new SKColor((byte)(255 * (10 - i) / 10),
0,
(byte)(255 * i / 10));
canvas.Save();
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.Scale(scale);
canvas.Translate(-pathBounds.MidX, -pathBounds.MidY);
canvas.DrawPath(path, fillPaint);
canvas.Restore();
scale *= 0.9f;
}
}
}
Kód také zobrazuje hvězdu o 10krát vícekrát, při každém snížení měřítka o 10 % a postupné změně barvy z červené na modrou: