La transformation d’échelle
Découvrez la transformation de mise à l’échelle skiaSharp pour mettre à l’échelle des objets à différentes tailles
Comme vous l’avez vu dans l’article Translate Transform , la transformation de traduction peut déplacer un objet graphique d’un emplacement à un autre. En revanche, la transformation d’échelle modifie la taille de l’objet graphique :
La transformation d’échelle entraîne également souvent le déplacement des coordonnées graphiques au fur et à mesure qu’elles sont plus grandes.
Plus tôt, vous avez vu deux formules de transformation qui décrivent les effets des facteurs de traduction et dx
dy
:
x' = x + dx
y' = y + dy
Les facteurs d’échelle et sx
sy
sont multipliés plutôt que additifs :
x' = sx · X
y' = sy · y
Les valeurs par défaut des facteurs de traduction sont 0 ; les valeurs par défaut des facteurs d’échelle sont 1.
La SKCanvas
classe définit quatre Scale
méthodes. La première Scale
méthode est pour les cas où vous souhaitez le même facteur de mise à l’échelle horizontale et verticale :
public void Scale (Single s)
Il s’agit de la mise à l’échelle isotropique , mise à l’échelle identique dans les deux sens. La mise à l’échelle isotropique conserve le rapport d’aspect de l’objet.
La deuxième Scale
méthode vous permet de spécifier différentes valeurs pour la mise à l’échelle horizontale et verticale :
public void Scale (Single sx, Single sy)
Cela entraîne une mise à l’échelle anisotropique .
La troisième Scale
méthode combine les deux facteurs de mise à l’échelle dans une valeur unique SKPoint
:
public void Scale (SKPoint size)
La quatrième Scale
méthode sera décrite sous peu.
La page Mise à l’échelle de base illustre la Scale
méthode. Le fichier BasicScalePage.xaml contient deux Slider
éléments qui vous permettent de sélectionner des facteurs de mise à l’échelle horizontal et vertical compris entre 0 et 10. Le fichier code-behind BasicScalePage.xaml.cs utilise ces valeurs pour appeler Scale
avant d’afficher un rectangle arrondi avec une ligne en pointillés et dimensionné pour ajuster un texte dans le coin supérieur gauche du canevas :
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);
}
}
Vous pouvez vous demander : Comment les facteurs de mise à l’échelle affectent-ils la valeur retournée par la MeasureText
méthode de SKPaint
? La réponse est : Pas du tout. Scale
est une méthode de SKCanvas
. Cela n’affecte rien que vous faites avec un SKPaint
objet tant que vous n’utilisez pas cet objet pour afficher quelque chose sur le canevas.
Comme vous pouvez le voir, tout dessiné après l’appel Scale
augmente proportionnellement :
Le texte, la largeur de la ligne en pointillés, la longueur des tirets de cette ligne, l’arrondi des angles et la marge de 10 pixels entre les bords gauche et supérieur du canevas et le rectangle arrondi sont tous soumis aux mêmes facteurs de mise à l’échelle.
Important
La plateforme Windows universelle ne restitue pas correctement le texte à l’échelle anisotropique.
La mise à l’échelle anisotropique entraîne la différence de largeur de trait pour les lignes alignées avec les axes horizontaux et verticaux. (Cela est également évident à partir de la première image de cette page.) Si vous ne souhaitez pas que la largeur du trait soit affectée par les facteurs de mise à l’échelle, définissez-la sur 0 et elle sera toujours d’un pixel, quel que soit le Scale
paramètre.
La mise à l’échelle est relative au coin supérieur gauche du canevas. C’est peut-être exactement ce que vous voulez, mais ce n’est peut-être pas le cas. Supposons que vous souhaitez positionner le texte et le rectangle ailleurs sur le canevas et que vous souhaitez le mettre à l’échelle par rapport à son centre. Dans ce cas, vous pouvez utiliser la quatrième version de la Scale
méthode, qui inclut deux paramètres supplémentaires pour spécifier le centre de mise à l’échelle :
public void Scale (Single sx, Single sy, Single px, Single py)
Les px
paramètres et py
les paramètres définissent un point parfois appelé centre de mise à l’échelle, mais dans la documentation SkiaSharp, on parle de point croisé dynamique. Il s’agit d’un point relatif au coin supérieur gauche du canevas qui n’est pas affecté par la mise à l’échelle. Toutes les mises à l’échelle se produisent par rapport à ce centre.
La page Mise à l’échelle centrée montre comment cela fonctionne. Le PaintSurface
gestionnaire est similaire au programme Échelle de base, sauf que la margin
valeur est calculée pour centrer le texte horizontalement, ce qui implique que le programme fonctionne le mieux en mode portrait :
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);
}
}
Le coin supérieur gauche du rectangle arrondi est positionné margin
en pixels à partir de la gauche du canevas et margin
des pixels du haut. Les deux derniers arguments de la Scale
méthode sont définis sur ces valeurs, ainsi que la largeur et la hauteur du texte, qui est également la largeur et la hauteur du rectangle arrondi. Cela signifie que toute la mise à l’échelle est relative au centre de ce rectangle :
Les Slider
éléments de ce programme ont une plage de -10 à 10. Comme vous pouvez le voir, les valeurs négatives de la mise à l’échelle verticale (par exemple, sur l’écran Android au centre) entraînent un retournement d’objets autour de l’axe horizontal qui passe par le centre de la mise à l’échelle. Les valeurs négatives de la mise à l’échelle horizontale (par exemple, dans l’écran UWP à droite) entraînent le retournement d’objets autour de l’axe vertical qui traverse le centre de la mise à l’échelle.
La version de la Scale
méthode avec des points croisés dynamiques est un raccourci pour une série de trois Translate
appels.Scale
Vous pouvez voir comment cela fonctionne en remplaçant la Scale
méthode dans la page Mise à l’échelle centrée par les éléments suivants :
canvas.Translate(-px, -py);
Il s’agit des points négatifs des coordonnées de point croisé dynamique.
Réexécutez le programme. Vous verrez que le rectangle et le texte sont décalés afin que le centre se trouve dans le coin supérieur gauche du canevas. Vous pouvez à peine le voir. Les curseurs ne fonctionnent pas bien sûr, car le programme n’est pas mis à l’échelle du tout.
Ajoutez maintenant l’appel de base Scale
(sans centre de mise à l’échelle) avant cet Translate
appel :
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);
Si vous êtes familiarisé avec cet exercice dans d’autres systèmes de programmation graphique, vous pouvez penser que c’est incorrect, mais ce n’est pas le cas. Skia gère les appels de transformation successifs un peu différemment de ce que vous connaissez peut-être.
Avec les appels successifs Scale
, Translate
le centre du rectangle arrondi se trouve toujours dans le coin supérieur gauche, mais vous pouvez maintenant l’adapter par rapport au coin supérieur gauche du canevas, qui est également le centre du rectangle arrondi.
Maintenant, avant cet Scale
appel, ajoutez un autre Translate
appel avec les valeurs de centrement :
canvas.Translate(px, py);
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);
Cela déplace le résultat mis à l’échelle vers la position d’origine. Ces trois appels sont équivalents à :
canvas.Scale(sx, sy, px, py);
Les transformations individuelles sont composées afin que la formule de transformation totale soit :
x' = sx · (x – px) + px
y' = sy · (y – py) + py
N’oubliez pas que les valeurs par défaut et sx
sy
sont 1. Il est facile de vous convaincre que le point pivot (px, py) n’est pas transformé par ces formules. Il reste dans le même emplacement par rapport au canevas.
Lorsque vous combinez et Scale
appelezTranslate
, l’ordre est important. Si l’élément Translate
vient après le Scale
, les facteurs de traduction sont mis à l’échelle efficacement par les facteurs de mise à l’échelle. Si la Translate
valeur est antérieure Scale
, les facteurs de traduction ne sont pas mis à l’échelle. Ce processus devient un peu plus clair (quoique plus mathématique) lorsque le sujet des matrices de transformation est introduit.
La SKPath
classe définit une propriété en lecture seule Bounds
qui retourne une SKRect
définition de l’étendue des coordonnées dans le chemin d’accès. Par exemple, lorsque la Bounds
propriété est obtenue à partir du chemin d’accès hendecagram créé précédemment, les propriétés et Top
les Left
propriétés du rectangle sont d’environ -100, et Bottom
les Right
propriétés sont d’environ 100, et les Width
Height
propriétés sont d’environ 200. (La plupart des valeurs réelles sont un peu moins parce que les points des étoiles sont définis par un cercle avec un rayon de 100, mais seul le point supérieur est parallèle aux axes horizontaux ou verticaux.)
La disponibilité de ces informations implique qu’il doit être possible de dériver l’échelle et de traduire des facteurs adaptés à la mise à l’échelle d’un chemin vers la taille du canevas. La page Mise à l’échelle anisotropique montre cela avec l’étoile à pointe de 11. Une échelle anisotropique signifie qu’elle est inégale dans les directions horizontales et verticales, ce qui signifie que l’étoile ne conservera pas son rapport d’aspect d’origine. Voici le code approprié dans le PaintSurface
gestionnaire :
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);
}
Le pathBounds
rectangle est obtenu près du haut de ce code, puis utilisé ultérieurement avec la largeur et la hauteur du canevas dans l’appel Scale
. Cet appel lui-même met à l’échelle les coordonnées du chemin lorsqu’il est rendu par l’appel DrawPath
, mais l’étoile est centrée dans le coin supérieur droit du canevas. Il doit être décalé vers le bas et vers la gauche. Il s’agit du travail de l’appel Translate
. Ces deux propriétés sont d’environ -100, de sorte que les facteurs de pathBounds
traduction sont d’environ 100. Étant donné que l’appel Translate
est après l’appel Scale
, ces valeurs sont mises à l’échelle efficacement par les facteurs de mise à l’échelle, de sorte qu’elles déplacent le centre de l’étoile au centre du canevas :
Une autre façon de réfléchir Scale
aux appels consiste Translate
à déterminer l’effet dans la séquence inverse : l’appel Translate
déplace le chemin afin qu’il devienne entièrement visible mais orienté dans le coin supérieur gauche du canevas. La Scale
méthode rend ensuite cette étoile plus grande par rapport au coin supérieur gauche.
En fait, il semble que l’étoile est un peu plus grande que la toile. Le problème est la largeur du trait. La Bounds
propriété de SKPath
indique les dimensions des coordonnées encodées dans le chemin d’accès, et c’est ce que le programme utilise pour l’adapter. Lorsque le chemin est rendu avec une largeur de trait particulière, le chemin rendu est plus grand que le canevas.
Pour résoudre ce problème, vous devez compenser cela. Une approche simple dans ce programme consiste à ajouter l’instruction suivante juste avant l’appel Scale
:
pathBounds.Inflate(strokePaint.StrokeWidth / 2,
strokePaint.StrokeWidth / 2);
Cela augmente le pathBounds
rectangle de 1,5 unités sur les quatre côtés. Il s’agit d’une solution raisonnable uniquement lorsque la jointure de trait est arrondie. Une jointure de mitreur peut être plus longue et est difficile à calculer.
Vous pouvez également utiliser une technique similaire avec du texte, comme le montre la page Texte anisotropique. Voici la partie pertinente du PaintSurface
gestionnaire de la 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);
}
Il s’agit d’une logique similaire, et le texte s’étend à la taille de la page en fonction du rectangle lié au texte retourné MeasureText
(qui est un peu plus grand que le texte réel) :
Si vous devez conserver les proportions des objets graphiques, vous devez utiliser la mise à l’échelle isotropique. La page Mise à l’échelle isotropique montre ceci pour l’étoile à pointe de 11. Conceptuellement, les étapes d’affichage d’un objet graphique au centre de la page avec mise à l’échelle isotropique sont les suivantes :
- Traduisez le centre de l’objet graphique dans le coin supérieur gauche.
- Mettez à l’échelle l’objet en fonction du minimum des dimensions de page horizontale et verticale divisées par les dimensions de l’objet graphique.
- Traduisez le centre de l’objet mis à l’échelle au centre de la page.
Les IsotropicScalingPage
étapes suivantes sont effectuées dans l’ordre inverse avant d’afficher l’étoile :
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;
}
}
}
Le code affiche également l’étoile 10 fois plus, chaque fois que le facteur de mise à l’échelle diminue de 10 % et change progressivement la couleur du rouge au bleu :