Пути и текст в SkiaSharp
Изучение пересечения путей и текста
В современных графических системах текстовые шрифты представляют собой коллекции контуров символов, как правило, определяются квадратичными кривыми Bézier. Следовательно, многие современные графические системы включают в себя объект для преобразования текстовых символов в графический путь.
Вы уже видели, что можно обчеркить контуры текстовых символов, а также заполнить их. Это позволяет отображать эти контуры символов с определенной шириной штриха и даже эффектом пути, как описано в статье "Эффекты пути". Но также можно преобразовать символьную строку в SKPath
объект. Это означает, что контуры текста можно использовать для обрезки с помощью методов, описанных в статье "Обрезка с путями и регионами ".
Помимо использования эффекта пути для росчерка контура символов, можно также создавать эффекты пути, основанные на пути, который является производным от строки символов, и вы можете даже объединить два эффекта:
В предыдущей статье о эффектах пути вы узнали, как GetFillPath
метод SKPaint
может получить контур штрихового пути. Этот метод также можно использовать с путями, производными от контуров символов.
Наконец, в этой статье демонстрируется еще одно пересечение путей и текста: DrawTextOnPath
метод SKCanvas
позволяет отображать текстовую строку таким образом, чтобы базовый план текста следил за кривым путем.
Преобразование текста в путь
Метод GetTextPath
SKPaint
преобразует символьную строку в SKPath
объект:
public SKPath GetTextPath (String text, Single x, Single y)
y
Аргументы x
указывают начальную точку базового плана левой части текста. Они играют ту же роль здесь, что и в методе DrawText
SKCanvas
. В пути базовый план левой части текста будет иметь координаты (x, y).
Метод GetTextPath
переклинается, если вы просто хотите заполнить или обвести результирующий путь. Обычный DrawText
метод позволяет сделать это. Этот GetTextPath
метод более полезен для других задач, связанных с путями.
Одна из этих задач — вырезка. Страница "Вырезка текста " создает путь вырезки на основе контуров символов слова "CODE". Этот путь растянут до размера страницы, чтобы закрепить растровое изображение, содержащее изображение исходного кода вырезки текста :
Конструктор ClippingTextPage
класса загружает растровое изображение, которое хранится в качестве внедренного ресурса в папке мультимедиа решения:
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);
}
}
...
}
Обработчик PaintSurface
начинается с создания объекта, подходящего SKPaint
для текста. Свойство Typeface
задано, а также TextSize
свойство, хотя для этого конкретного приложения TextSize
свойство является исключительно произвольным. Кроме того, обратите внимание, что параметр отсутствует Style
.
Параметры TextSize
и Style
параметры свойств не требуются, так как этот SKPaint
объект используется исключительно для GetTextPath
вызова с помощью текстовой строки "CODE". Затем обработчик измеряет результирующий SKPath
объект и применяет три преобразования для его центра и масштабирования до размера страницы. Затем путь можно задать в качестве пути обрезки:
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)));
}
}
После установки пути обрезки можно отобразить растровое изображение, и оно будет обрезано к контурам символов. Обратите внимание на использование AspectFill
метода SKRect
, который вычисляет прямоугольник для заполнения страницы при сохранении пропорции.
Страница эффектов текстового пути преобразует один амперсанд в путь, чтобы создать эффект 1D-пути. Затем объект краски с этим эффектом пути используется для росчерка контура более крупной версии того же символа:
Большая часть работы в классе выполняется в TextPathEffectPath
полях и конструкторе. Два объекта, определенные как поля, используются в двух SKPaint
разных целях: первый (именованный textPathPaint
) используется для преобразования амперсанда с TextSize
50 в путь для эффекта 1D-пути. Второй (textPaint
) используется для отображения более крупной версии амперсанда с этим эффектом пути. По этой причине для этого второго объекта краски задано Stroke
значение, но StrokeWidth
свойство не задано, Style
так как это свойство не требуется при использовании эффекта 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);
}
...
}
Конструктор сначала использует textPathPaint
объект для измерения амперсанда с TextSize
50. Затем отрицательные координаты центра этого прямоугольника передаются GetTextPath
методу для преобразования текста в путь. Результирующий путь имеет точку (0, 0) в центре символа, которая идеально подходит для эффекта 1D-пути.
Возможно, SKPathEffect
объект, созданный в конце конструктора, может быть задан свойством PathEffect
textPaint
вместо сохранения в качестве поля. Но это оказалось не работать очень хорошо, потому что оно исказило результаты MeasureText
вызова в обработчике PaintSurface
:
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);
}
}
Этот MeasureText
вызов используется для центра символа на странице. Чтобы избежать проблем, PathEffect
свойство присваивается объекту краски после измерения текста, но до его отображения.
Контуры символьных контуров
GetFillPath
Обычно метод преобразует один путь в другой путем применения свойств краски, в частности ширины штриха SKPaint
и эффекта пути. При использовании без эффектов GetFillPath
пути эффективно создает путь, описывающий другой путь. Это было показано на странице "Контур пути" в статье "Эффекты пути".
Вы также можете вызвать GetFillPath
путь, возвращенный из GetTextPath
, но сначала вы не можете быть полностью уверены, что это будет выглядеть.
Страница "Структура символов " демонстрирует метод. Весь соответствующий код находится в PaintSurface
обработчике CharacterOutlineOutlinesPage
класса.
Конструктор начинается с создания SKPaint
объекта с именем textPaint
свойства TextSize
на основе размера страницы. Это преобразуется в путь с помощью GetTextPath
метода. Аргументы координат для GetTextPath
эффективного центра пути на экране:
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);
}
}
}
}
}
Затем PaintSurface
обработчик создает новый путь с именем outlinePath
. Это становится конечным путем в вызове GetFillPath
. Свойство StrokeWidth
25 приводит outlinePath
к описанию контура 25-пиксельного пути, прорезающего текстовые символы. Затем этот путь отображается красным цветом с шириной штриха 5:
Внимательно посмотрите, и вы увидите перекрытия, где контур пути делает острый угол. Это обычные артефакты этого процесса.
Текст по пути
Текст обычно отображается на горизонтальном базовом уровне. Текст можно повернуть для вертикального или диагонали, но базовый план по-прежнему является прямой линией.
Однако есть времена, когда требуется, чтобы текст выполнялся вдоль кривой. Это цель DrawTextOnPath
метода SKCanvas
:
public Void DrawTextOnPath (String text, SKPath path, Single hOffset, Single vOffset, SKPaint paint)
Текст, указанный в первом аргументе, выполняется по пути, указанному в качестве второго аргумента. Текст можно начать с смещения с начала пути с аргументом hOffset
. Обычно путь формирует базовый план текста: по возрастанию текста находятся на одной стороне пути, а нисходящие тексты находятся в другой. Но можно смещать текстовые базовые показатели из пути с помощью аргумента vOffset
.
Этот метод не имеет возможности предоставить рекомендации по настройке TextSize
свойства SKPaint
, чтобы размер текста идеально выполнялся с начала пути к концу. Иногда вы можете определить размер текста самостоятельно. В других случаях необходимо использовать функции измерения пути, описанные в следующей статье о сведениях о пути и перечислении.
Программа циклического текста упаковывает текст вокруг круга. Это легко определить окружность круга, поэтому легко размер текста, чтобы точно соответствовать. Обработчик PaintSurface
CircularTextPage
класса вычисляет радиус круга на основе размера страницы. Этот круг становится 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);
}
}
}
}
Затем TextSize
свойство textPaint
настраивается таким образом, чтобы ширина текста соответствовала окружности круга:
Сам текст был выбран, чтобы быть несколько циклическим, а также: слово "круг" является как предметом предложения, так и объектом предварительной фразы.