Chemins et texte dans SkiaSharp
Explorer l’intersection des chemins d’accès et du texte
Dans les systèmes graphiques modernes, les polices de texte sont des collections de contours de caractères, généralement définies par des courbes de Bézier quadratique. Par conséquent, de nombreux systèmes graphiques modernes incluent une fonctionnalité permettant de convertir des caractères de texte en chemin d’accès graphique.
Vous avez déjà vu que vous pouvez traiter les contours des caractères de texte, ainsi que les remplir. Cela vous permet d’afficher ces contours de caractères avec une largeur de trait particulière et même un effet de chemin comme décrit dans l’article Effets du chemin d’accès. Mais il est également possible de convertir une chaîne de caractères en objet SKPath
. Cela signifie que les contours de texte peuvent être utilisés pour la capture avec des techniques décrites dans l’article Clipping with Paths and Regions .
Outre l’utilisation d’un effet de chemin d’accès pour traiter un contour de caractère, vous pouvez également créer des effets de chemin d’accès basés sur un chemin dérivé d’une chaîne de caractères, et vous pouvez même combiner les deux effets :
Dans l’article précédent sur les effets de chemin d’accès, vous avez vu comment la GetFillPath
méthode de SKPaint
peut obtenir un contour d’un chemin tracé. Vous pouvez également utiliser cette méthode avec des chemins dérivés de contours de caractères.
Enfin, cet article illustre une autre intersection de chemins d’accès et de texte : la DrawTextOnPath
méthode de vous permet d’afficher une chaîne de SKCanvas
texte afin que la ligne de base du texte suit un chemin courbé.
Conversion de texte en chemin
La GetTextPath
méthode de conversion d’une chaîne de SKPaint
caractères en objet SKPath
:
public SKPath GetTextPath (String text, Single x, Single y)
Les x
arguments et y
les arguments indiquent le point de départ de la ligne de base du côté gauche du texte. Ils jouent ici le même rôle que dans la DrawText
méthode de SKCanvas
. Dans le chemin, la ligne de base du côté gauche du texte aura les coordonnées (x, y).
La GetTextPath
méthode est excessive si vous souhaitez simplement remplir ou traiter le chemin résultant. La méthode normale DrawText
vous permet de le faire. La GetTextPath
méthode est plus utile pour d’autres tâches impliquant des chemins d’accès.
L’une de ces tâches consiste à découper. La page Texte de découpage crée un chemin de découpage en fonction des contours de caractères du mot « CODE ». Ce chemin d’accès est étendu à la taille de la page pour découper une bitmap qui contient une image du code source du texte de découpage :
Le ClippingTextPage
constructeur de classe charge la bitmap stockée en tant que ressource incorporée dans le dossier Media de la solution :
public class ClippingTextPage : ContentPage
{
SKBitmap bitmap;
public ClippingTextPage()
{
Title = "Clipping Text";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
string resourceID = "SkiaSharpFormsDemos.Media.PageOfCode.png";
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream(resourceID))
{
bitmap = SKBitmap.Decode(stream);
}
}
...
}
Le PaintSurface
gestionnaire commence par créer un SKPaint
objet adapté au texte. La Typeface
propriété est définie ainsi que le TextSize
, bien que pour cette application particulière, la TextSize
propriété soit purement arbitraire. Notez également qu’il n’existe aucun Style
paramètre.
Les TextSize
paramètres de propriété ne Style
sont pas nécessaires, car cet SKPaint
objet est utilisé uniquement pour l’appel à l’aide GetTextPath
de la chaîne de texte « CODE ». Le gestionnaire mesure ensuite l’objet résultant SKPath
et applique trois transformations pour le centrer et l’adapter à la taille de la page. Le chemin d’accès peut ensuite être défini comme chemin d’accès de découpage :
public class ClippingTextPage : ContentPage
{
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.Blue);
using (SKPaint paint = new SKPaint())
{
paint.Typeface = SKTypeface.FromFamilyName(null, SKTypefaceStyle.Bold);
paint.TextSize = 10;
using (SKPath textPath = paint.GetTextPath("CODE", 0, 0))
{
// Set transform to center and enlarge clip path to window height
SKRect bounds;
textPath.GetTightBounds(out bounds);
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.Scale(info.Width / bounds.Width, info.Height / bounds.Height);
canvas.Translate(-bounds.MidX, -bounds.MidY);
// Set the clip path
canvas.ClipPath(textPath);
}
}
// Reset transforms
canvas.ResetMatrix();
// Display bitmap to fill window but maintain aspect ratio
SKRect rect = new SKRect(0, 0, info.Width, info.Height);
canvas.DrawBitmap(bitmap,
rect.AspectFill(new SKSize(bitmap.Width, bitmap.Height)));
}
}
Une fois le chemin d’accès de découpage défini, l’image bitmap peut être affichée, et elle est découpée dans les contours des caractères. Notez l’utilisation de la AspectFill
méthode de SKRect
calcul d’un rectangle pour remplir la page tout en conservant le rapport d’aspect.
La page Effet de chemin de texte convertit un caractère d’ampersand unique en un chemin d’accès pour créer un effet de chemin d’accès 1D. Un objet de peinture avec cet effet de chemin d’accès est ensuite utilisé pour traiter le contour d’une version plus grande de ce même caractère :
Une grande partie du travail de la TextPathEffectPath
classe se produit dans les champs et le constructeur. Les deux SKPaint
objets définis en tant que champs sont utilisés à deux fins différentes : le premier (nommé textPathPaint
) est utilisé pour convertir l’ampersand avec un TextSize
chemin d’accès de 50 en chemin d’accès 1D. Le deuxième (textPaint
) est utilisé pour afficher la version plus grande de l’ampersand avec cet effet de chemin d’accès. Pour cette raison, le Style
deuxième objet de peinture est défini Stroke
sur , mais la StrokeWidth
propriété n’est pas définie, car cette propriété n’est pas nécessaire lors de l’utilisation d’un effet de chemin d’accès 1D :
public class TextPathEffectPage : ContentPage
{
const string character = "@";
const float littleSize = 50;
SKPathEffect pathEffect;
SKPaint textPathPaint = new SKPaint
{
TextSize = littleSize
};
SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Black
};
public TextPathEffectPage()
{
Title = "Text Path Effect";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
// Get the bounds of textPathPaint
SKRect textPathPaintBounds = new SKRect();
textPathPaint.MeasureText(character, ref textPathPaintBounds);
// Create textPath centered around (0, 0)
SKPath textPath = textPathPaint.GetTextPath(character,
-textPathPaintBounds.MidX,
-textPathPaintBounds.MidY);
// Create the path effect
pathEffect = SKPathEffect.Create1DPath(textPath, littleSize, 0,
SKPath1DPathEffectStyle.Translate);
}
...
}
Le constructeur utilise d’abord l’objet textPathPaint
pour mesurer l’ampersand avec une TextSize
valeur de 50. Les négatifs des coordonnées centrale de ce rectangle sont ensuite passés à la GetTextPath
méthode pour convertir le texte en chemin d’accès. Le chemin résultant a le point (0, 0) au centre du caractère, qui est idéal pour un effet de chemin 1D.
Vous pensez peut-être que l’objet SKPathEffect
créé à la fin du constructeur peut être défini sur la PathEffect
propriété du lieu d’être textPaint
enregistré en tant que champ. Mais cela s’est avéré ne pas fonctionner très bien parce qu’il a déformé les résultats de l’appel MeasureText
dans le PaintSurface
gestionnaire :
public class TextPathEffectPage : ContentPage
{
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
// Set textPaint TextSize based on screen size
textPaint.TextSize = Math.Min(info.Width, info.Height);
// Do not measure the text with PathEffect set!
SKRect textBounds = new SKRect();
textPaint.MeasureText(character, ref textBounds);
// Coordinates to center text on screen
float xText = info.Width / 2 - textBounds.MidX;
float yText = info.Height / 2 - textBounds.MidY;
// Set the PathEffect property and display text
textPaint.PathEffect = pathEffect;
canvas.DrawText(character, xText, yText, textPaint);
}
}
Cet MeasureText
appel est utilisé pour centrer le caractère sur la page. Pour éviter les problèmes, la PathEffect
propriété est définie sur l’objet de peinture une fois que le texte a été mesuré, mais avant son affichage.
Contours des contours de caractères
Normalement, la GetFillPath
méthode de conversion d’un chemin à un autre en appliquant des propriétés de SKPaint
peinture, notamment la largeur et l’effet de tracé. Lorsqu’il est utilisé sans effets de chemin d’accès, GetFillPath
crée efficacement un chemin qui décrit un autre chemin. Cela a été illustré dans la page Appuyer pour décrire le chemin d’accès dans l’article Effets du chemin d’accès.
Vous pouvez également appeler GetFillPath
le chemin d’accès retourné, GetTextPath
mais au début, vous ne serez peut-être pas entièrement sûr de ce qui ressemblerait.
La page Contours du contour des caractères illustre la technique. Tout le code approprié se trouve dans le PaintSurface
gestionnaire de la CharacterOutlineOutlinesPage
classe.
Le constructeur commence par créer un SKPaint
objet nommé textPaint
avec une TextSize
propriété en fonction de la taille de la page. Cette opération est convertie en chemin d’accès à l’aide de la GetTextPath
méthode. Arguments de coordonnées pour GetTextPath
centrer efficacement le chemin d’accès à l’écran :
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint textPaint = new SKPaint())
{
// Set Style for the character outlines
textPaint.Style = SKPaintStyle.Stroke;
// Set TextSize based on screen size
textPaint.TextSize = Math.Min(info.Width, info.Height);
// Measure the text
SKRect textBounds = new SKRect();
textPaint.MeasureText("@", ref textBounds);
// Coordinates to center text on screen
float xText = info.Width / 2 - textBounds.MidX;
float yText = info.Height / 2 - textBounds.MidY;
// Get the path for the character outlines
using (SKPath textPath = textPaint.GetTextPath("@", xText, yText))
{
// Create a new path for the outlines of the path
using (SKPath outlinePath = new SKPath())
{
// Convert the path to the outlines of the stroked path
textPaint.StrokeWidth = 25;
textPaint.GetFillPath(textPath, outlinePath);
// Stroke that new path
using (SKPaint outlinePaint = new SKPaint())
{
outlinePaint.Style = SKPaintStyle.Stroke;
outlinePaint.StrokeWidth = 5;
outlinePaint.Color = SKColors.Red;
canvas.DrawPath(outlinePath, outlinePaint);
}
}
}
}
}
Le PaintSurface
gestionnaire crée ensuite un chemin nommé outlinePath
. Il devient le chemin d’accès de destination dans l’appel à GetFillPath
. La StrokeWidth
propriété 25 provoque outlinePath
la description du contour d’un chemin d’accès de 25 pixels qui a trait aux caractères de texte. Ce chemin s’affiche ensuite en rouge avec une largeur de trait de 5 :
Regardez attentivement et vous verrez des chevauchements où le contour du chemin fait un coin aigu. Il s’agit d’artefacts normaux de ce processus.
Texte le long d’un chemin
Le texte est normalement affiché sur une ligne de base horizontale. Le texte peut être pivoté pour s’exécuter verticalement ou en diagonale, mais la ligne de base est toujours une ligne droite.
Toutefois, il y a des moments où vous souhaitez que le texte s’exécute le long d’une courbe. Il s’agit de l’objectif de la DrawTextOnPath
méthode de SKCanvas
:
public Void DrawTextOnPath (String text, SKPath path, Single hOffset, Single vOffset, SKPaint paint)
Le texte spécifié dans le premier argument est effectué pour s’exécuter le long du chemin spécifié comme deuxième argument. Vous pouvez commencer le texte à un décalage à partir du début du chemin d’accès avec l’argument hOffset
. Normalement, le chemin forme la ligne de base du texte : les croissants de texte se trouvent sur un côté du chemin, et les descendeurs de texte se trouvent sur l’autre. Toutefois, vous pouvez décaler la ligne de base de texte du chemin d’accès avec l’argument vOffset
.
Cette méthode n’a pas la possibilité de fournir des conseils sur la définition de la TextSize
propriété de SKPaint
façon à ce que le texte soit parfaitement dimensionné pour s’exécuter du début du chemin jusqu’à la fin. Parfois, vous pouvez déterminer cette taille de texte par vous-même. D’autres fois, vous devez utiliser les fonctions de mesure du chemin d’accès pour être décrites dans l’article suivant sur les informations de chemin d’accès et l’énumération.
Le programme Texte circulaire encapsule le texte autour d’un cercle. Il est facile de déterminer la circonférence d’un cercle, de sorte qu’il est facile de dimensionner le texte pour s’adapter exactement. Le PaintSurface
gestionnaire de la CircularTextPage
classe calcule un rayon d’un cercle en fonction de la taille de la page. Ce cercle devient circularPath
:
public class CircularTextPage : ContentPage
{
const string text = "xt in a circle that shapes the te";
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPath circularPath = new SKPath())
{
float radius = 0.35f * Math.Min(info.Width, info.Height);
circularPath.AddCircle(info.Width / 2, info.Height / 2, radius);
using (SKPaint textPaint = new SKPaint())
{
textPaint.TextSize = 100;
float textWidth = textPaint.MeasureText(text);
textPaint.TextSize *= 2 * 3.14f * radius / textWidth;
canvas.DrawTextOnPath(text, circularPath, 0, 0, textPaint);
}
}
}
}
La TextSize
propriété de textPaint
l’objet est ensuite ajustée afin que la largeur du texte corresponde à la circonférence du cercle :
Le texte lui-même a été choisi pour être un peu circulaire : le mot « cercle » est à la fois le sujet de la phrase et l’objet d’une expression prépositionnelle.