Trasformazione di ridimensionamento
Individuare la trasformazione della scala SkiaSharp per ridimensionare gli oggetti in varie dimensioni
Come si è visto nell'articolo Traduzione trasformazione , la trasformazione traduci può spostare un oggetto grafico da una posizione a un'altra. Al contrario, la trasformazione della scala modifica le dimensioni dell'oggetto grafico:
La trasformazione della scala spesso comporta lo spostamento delle coordinate grafiche man mano che vengono rese più grandi.
In precedenza sono state illustrate due formule di trasformazione che descrivono gli effetti dei fattori di traslazione di dx
e dy
:
x' = x + dx
y' = y + dy
I fattori di scala di sx
e sy
sono moltiplicativi anziché additivi:
x' = sx · X
y' = sy · Y
I valori predefiniti dei fattori di conversione sono 0; i valori predefiniti dei fattori di scala sono 1.
La SKCanvas
classe definisce quattro Scale
metodi. Il primo Scale
metodo è per i casi in cui si desidera lo stesso fattore di ridimensionamento orizzontale e verticale:
public void Scale (Single s)
Questo è noto come ridimensionamento isotropico - ridimensionamento che è lo stesso in entrambe le direzioni. La scalabilità isotropica mantiene le proporzioni dell'oggetto.
Il secondo Scale
metodo consente di specificare valori diversi per la scalabilità orizzontale e verticale:
public void Scale (Single sx, Single sy)
Questo comporta un ridimensionamento anisotropico .
Il terzo Scale
metodo combina i due fattori di ridimensionamento in un singolo SKPoint
valore:
public void Scale (SKPoint size)
Il quarto Scale
metodo verrà descritto a breve.
La pagina Scala di base illustra il Scale
metodo . Il file BasicScalePage.xaml contiene due Slider
elementi che consentono di selezionare fattori di ridimensionamento orizzontale e verticale compresi tra 0 e 10. Il file code-behind BasicScalePage.xaml.cs usa tali valori da chiamare Scale
prima di visualizzare un rettangolo arrotondato con una linea tratteggiata e ridimensionato per adattare un testo nell'angolo superiore sinistro dell'area di disegno:
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);
}
}
Ci si potrebbe chiedere: in che modo i fattori di ridimensionamento influiscono sul valore restituito dal MeasureText
metodo di SKPaint
? La risposta è: Non affatto. Scale
è un metodo di SKCanvas
. Non influisce su qualsiasi operazione eseguita con un SKPaint
oggetto fino a quando non si usa tale oggetto per eseguire il rendering di un elemento nell'area di disegno.
Come si può notare, tutto ciò che viene disegnato dopo la Scale
chiamata aumenta proporzionalmente:
Il testo, la larghezza della linea tratteggiata, la lunghezza dei trattini in tale linea, l'arrotondamento degli angoli e il margine di 10 pixel tra i bordi sinistro e superiore dell'area di disegno e il rettangolo arrotondato sono tutti soggetti agli stessi fattori di ridimensionamento.
Importante
Il piattaforma UWP (Universal Windows Platform) non esegue correttamente il rendering di testo a scalabilità anisotropica.
Il ridimensionamento anisotropico fa sì che la larghezza del tratto diventi diversa per le linee allineate agli assi orizzontali e verticali. Questo è evidente anche dalla prima immagine in questa pagina. Se non si vuole che la larghezza del tratto sia influenzata dai fattori di ridimensionamento, impostarla su 0 e sarà sempre un pixel largo indipendentemente dall'impostazione Scale
.
Il ridimensionamento è relativo all'angolo superiore sinistro dell'area di disegno. Questo potrebbe essere esattamente quello che vuoi, ma potrebbe non essere. Si supponga di voler posizionare il testo e il rettangolo in un'altra posizione nell'area di disegno e di ridimensionarlo rispetto al centro. In tal caso è possibile usare la quarta versione del Scale
metodo , che include due parametri aggiuntivi per specificare il centro del ridimensionamento:
public void Scale (Single sx, Single sy, Single px, Single py)
I px
parametri e py
definiscono un punto a volte denominato centro di ridimensionamento, ma nella documentazione skiaSharp viene definito punto pivot. Si tratta di un punto relativo all'angolo superiore sinistro dell'area di disegno che non è interessato dal ridimensionamento. Tutto il ridimensionamento si verifica in relazione al centro.
La pagina Scala allineata al centro mostra come funziona. Il PaintSurface
gestore è simile al programma Di base, ad eccezione del fatto che il margin
valore viene calcolato al centro del testo orizzontalmente, il che implica che il programma funziona meglio in modalità verticale:
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);
}
}
L'angolo superiore sinistro del rettangolo arrotondato è posizionato in margin
pixel a sinistra dell'area di disegno e margin
dei pixel dall'alto. Gli ultimi due argomenti del Scale
metodo vengono impostati su tali valori più la larghezza e l'altezza del testo, che è anche la larghezza e l'altezza del rettangolo arrotondato. Ciò significa che tutto il ridimensionamento è relativo al centro del rettangolo:
Gli Slider
elementi di questo programma hanno un intervallo compreso tra -10 e 10. Come si può notare, i valori negativi della scalabilità verticale (ad esempio sullo schermo Android al centro) causano il capovolgimento degli oggetti intorno all'asse orizzontale che passa attraverso il centro della scalabilità. I valori negativi della scalabilità orizzontale (ad esempio nella schermata UWP a destra) causano lo scorrimento degli oggetti attorno all'asse verticale che passa attraverso il centro del ridimensionamento.
La versione del Scale
metodo con punti pivot è un collegamento per una serie di tre Translate
chiamate e Scale
. È possibile visualizzare il funzionamento di questo metodo sostituendo il Scale
metodo nella pagina Scala al centro con quanto segue:
canvas.Translate(-px, -py);
Si tratta dei negativi delle coordinate del punto pivot.
Eseguire nuovamente il programma. Si noterà che il rettangolo e il testo vengono spostati in modo che il centro si trovi nell'angolo superiore sinistro dell'area di disegno. È possibile vederlo a malapena. I dispositivi di scorrimento non funzionano naturalmente perché ora il programma non viene ridimensionato affatto.
Aggiungere ora la chiamata di base Scale
(senza un centro di scalabilità) prima di tale Translate
chiamata:
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);
Se hai familiarità con questo esercizio in altri sistemi di programmazione grafica, potresti pensare che sia sbagliato, ma non lo è. Skia gestisce le chiamate di trasformazione successive in modo leggermente diverso da quello con cui si potrebbe avere familiarità.
Con le chiamate e Translate
successiveScale
, il centro del rettangolo arrotondato è ancora nell'angolo superiore sinistro, ma è ora possibile ridimensionarlo rispetto all'angolo superiore sinistro dell'area di disegno, che è anche il centro del rettangolo arrotondato.
A questo punto, prima della Scale
chiamata, aggiungere un'altra Translate
chiamata con i valori centrati:
canvas.Translate(px, py);
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);
In questo modo il risultato ridimensionato viene spostato nella posizione originale. Queste tre chiamate sono equivalenti a:
canvas.Scale(sx, sy, px, py);
Le singole trasformazioni vengono composte in modo che la formula di trasformazione totale sia:
x' = sx · (x – px) + px
y' = sy · (y – py) + py
Tenere presente che i valori predefiniti di sx
e sy
sono 1. È facile convincere se stessi che il punto pivot (px, py) non è trasformato da queste formule. Rimane nella stessa posizione rispetto all'area di disegno.
Quando si combinano Translate
e Scale
si chiama, l'ordine è importante. Se viene Translate
dopo , Scale
i fattori di traslazione vengono effettivamente ridimensionati dai fattori di ridimensionamento. Se precede Translate
Scale
, i fattori di conversione non vengono ridimensionati. Questo processo diventa un po 'più chiaro (anche se più matematico) quando viene introdotto il soggetto delle matrici di trasformazione.
La SKPath
classe definisce una proprietà di sola Bounds
lettura che restituisce un oggetto SKRect
che definisce l'extent delle coordinate nel percorso. Ad esempio, quando la Bounds
proprietà viene ottenuta dal percorso hendecagram creato in precedenza, le Left
proprietà e Top
del rettangolo sono circa -100, le Right
proprietà e Bottom
sono circa 100 e le Width
proprietà e Height
sono circa 200. La maggior parte dei valori effettivi è leggermente inferiore perché i punti delle stelle sono definiti da un cerchio con un raggio di 100, ma solo il punto superiore è parallelo con gli assi orizzontali o verticali.
La disponibilità di queste informazioni implica che dovrebbe essere possibile derivare la scala e tradurre i fattori adatti per ridimensionare un percorso alle dimensioni dell'area di disegno. La pagina Scala anisotropica dimostra questo con la stella a 11 punte. Una scala anisotropica significa che è diverso nelle direzioni orizzontali e verticali, il che significa che la stella non manterrà le proporzioni originali. Ecco il codice pertinente nel PaintSurface
gestore:
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);
}
Il pathBounds
rettangolo viene ottenuto nella parte superiore di questo codice e quindi usato in seguito con la larghezza e l'altezza dell'area di disegno nella Scale
chiamata. Tale chiamata in modo autonomo ridimensiona le coordinate del percorso quando viene eseguito il DrawPath
rendering dalla chiamata, ma la stella verrà allineata al centro nell'angolo superiore destro dell'area di disegno. Deve essere spostato verso il basso e verso sinistra. Questo è il processo della Translate
chiamata. Queste due proprietà di sono circa -100, quindi i fattori di pathBounds
traslazione sono circa 100. Poiché la Translate
chiamata è dopo la Scale
chiamata, tali valori vengono ridimensionati in modo efficace in base ai fattori di ridimensionamento, in modo da spostare il centro della stella al centro dell'area di disegno:
Un altro modo per pensare alle Scale
chiamate e Translate
consiste nel determinare l'effetto nella sequenza inversa: la Translate
chiamata sposta il percorso in modo che diventi completamente visibile ma orientato nell'angolo superiore sinistro dell'area di disegno. Il Scale
metodo rende quindi la stella più grande rispetto all'angolo superiore sinistro.
In realtà, sembra che la stella sia un po ' più grande della tela. Il problema è la larghezza del tratto. La Bounds
proprietà di SKPath
indica le dimensioni delle coordinate codificate nel percorso ed è ciò che il programma usa per ridimensionarlo. Quando viene eseguito il rendering del percorso con una particolare larghezza del tratto, il percorso sottoposto a rendering è maggiore dell'area di disegno.
Per risolvere questo problema, è necessario compensare questo problema. Un approccio semplice in questo programma consiste nell'aggiungere l'istruzione seguente subito prima della Scale
chiamata:
pathBounds.Inflate(strokePaint.StrokeWidth / 2,
strokePaint.StrokeWidth / 2);
In questo modo il pathBounds
rettangolo viene a 1,5 unità su tutti e quattro i lati. Si tratta di una soluzione ragionevole solo quando il tratto join viene arrotondato. Un miter join può essere più lungo ed è difficile da calcolare.
È anche possibile utilizzare una tecnica simile con il testo, come illustrato nella pagina Anisotropic Text . Ecco la parte pertinente del PaintSurface
gestore della AnisotropicTextPage
classe :
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);
}
È una logica simile e il testo si espande fino alla dimensione della pagina in base al rettangolo dei limiti di testo restituito ( MeasureText
che è leggermente più grande del testo effettivo):
Se è necessario mantenere le proporzioni degli oggetti grafici, è consigliabile usare il ridimensionamento isotropico. La pagina Isotropic Scaling illustra questo valore per la stella a 11 punte. Concettualmente, i passaggi per la visualizzazione di un oggetto grafico al centro della pagina con ridimensionamento isotropico sono:
- Convertire il centro dell'oggetto grafico nell'angolo superiore sinistro.
- Scalare l'oggetto in base alle dimensioni minime delle pagine orizzontali e verticali divise per le dimensioni dell'oggetto grafico.
- Convertire il centro dell'oggetto ridimensionato al centro della pagina.
IsotropicScalingPage
Esegue questi passaggi in ordine inverso prima di visualizzare la stella:
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;
}
}
}
Il codice visualizza anche la stella 10 volte più volte, ogni volta che diminuisce il fattore di ridimensionamento del 10% e cambiando progressivamente il colore da rosso a blu: