SkiaSharp'ta Yollar ve Metin
Yolların ve metnin kesişimini keşfetme
Modern grafik sistemlerinde metin yazı tipleri genellikle ikinci dereceden Bézier eğrileri tarafından tanımlanan karakter ana hatlarından oluşan koleksiyonlardır. Sonuç olarak, birçok modern grafik sistemi metin karakterlerini grafik yoluna dönüştürmek için bir özellik içerir.
Metin karakterlerinin ana hatlarını konturlayabileceğinizi ve doldurabileceğinizi zaten gördünüz. Bu, yol efektleri makalesinde açıklandığı gibi bu karakter ana hatlarını belirli bir vuruş genişliği ve hatta yol efektiyle görüntülemenizi sağlar. Ancak bir karakter dizesini nesneye SKPath
dönüştürmek de mümkündür. Bu, metin ana hatlarının Yollar ve Bölgeler ile Kırpma makalesinde açıklanan tekniklerle kırpma için kullanılabileceğini gösterir.
Karakter ana hattına vuruş yapmak için yol efekti kullanmanın yanı sıra, karakter dizesinden türetilen bir yolu temel alan yol efektleri de oluşturabilir ve hatta iki efekti birleştirebilirsiniz:
Yol Efektleri ile ilgili önceki makalede, yönteminin GetFillPath
SKPaint
vuruşlu bir yolun ana hattını nasıl edinebileceğini gördünüz. Bu yöntemi karakter ana hatlarından türetilen yollarla da kullanabilirsiniz.
Son olarak, bu makalede yollar ve metnin başka bir kesişimi gösterilmektedir: DrawTextOnPath
yöntemi SKCanvas
, metnin taban çizgisinin eğri bir yolu izlemesi için bir metin dizesi görüntülemenizi sağlar.
Metinden Yola Dönüştürme
GetTextPath
yöntemi SKPaint
bir karakter dizesini nesneye SKPath
dönüştürür:
public SKPath GetTextPath (String text, Single x, Single y)
x
ve y
bağımsız değişkenleri, metnin sol tarafının taban çizgisinin başlangıç noktasını gösterir. Burada yönteminde DrawText
SKCanvas
olduğu gibi aynı rolü oynarlar. Yol içinde, metnin sol tarafının taban çizgisi koordinatlara (x, y) sahip olur.
Yalnızca sonuçta elde edilen GetTextPath
yolu doldurmak veya konturlamak istiyorsanız, yöntem fazlalıktır. Normal DrawText
yöntem bunu yapmanızı sağlar. GetTextPath
yöntemi, yollar içeren diğer görevler için daha kullanışlıdır.
Bu görevlerden biri kırpmadır. Kırpma Metni sayfası, "CODE" sözcüğünün karakter ana hatlarını temel alan bir kırpma yolu oluşturur. Bu yol, Kırpma Metni kaynak kodunun görüntüsünü içeren bir bit eşlemi kırpmak için sayfanın boyutuna kadar uzatılır:
Sınıf oluşturucu, ClippingTextPage
çözümün Media klasöründe katıştırılmış kaynak olarak depolanan bit eşlemi yükler:
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);
}
}
...
}
İşleyici, PaintSurface
metne uygun bir SKPaint
nesne oluşturarak başlar. Typeface
özelliği ve TextSize
olarak ayarlanır, ancak bu uygulama TextSize
için özellik tamamen rastgeledir. Ayrıca ayar olmadığına Style
da dikkat edin.
TextSize
Style
Ve özellik ayarları gerekli değildir çünkü bu SKPaint
nesne yalnızca "CODE" metin dizesi kullanılarak çağrı için GetTextPath
kullanılır. İşleyici daha sonra sonuçta SKPath
elde edilen nesneyi ölçer ve ortalayıp sayfanın boyutuna ölçeklendirmek için üç dönüşüm uygular. Yol daha sonra kırpma yolu olarak ayarlanabilir:
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)));
}
}
Kırpma yolu ayarlandıktan sonra bit eşlem görüntülenebilir ve karakter ana hatlarına kırpılır. En boy oranını korurken sayfayı AspectFill
doldurmak için bir dikdörtgen hesaplayan yönteminin SKRect
kullanıldığına dikkat edin.
Metin Yolu Efekti sayfası, 1B yol efekti oluşturmak için tek bir ve işareti karakterini yola dönüştürür. Daha sonra bu yol efektine sahip bir paint nesnesi, aynı karakterin daha büyük bir sürümünün ana hattını vuruş yapmak için kullanılır:
Sınıfındaki işin TextPathEffectPath
büyük bir kısmı alanlarda ve oluşturucuda gerçekleşir. Alan olarak tanımlanan iki SKPaint
nesne iki farklı amaç için kullanılır: İlk (adlandırılmış textPathPaint
) ve işareti TextSize
50 olan ve işaretini 1B yol efekti için bir yola dönüştürmek için kullanılır. İkinci (textPaint
), ve işaretin daha büyük sürümünü bu yol etkisiyle görüntülemek için kullanılır. Bu nedenle, Style
bu ikinci paint nesnesinin öğesi olarak ayarlanır Stroke
, ancak StrokeWidth
1B yol efekti kullanılırken bu özellik gerekli olmadığından özelliği ayarlanmaz:
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);
}
...
}
Oluşturucu ilk olarak ve işaretini 50 ile TextSize
ölçmek için nesnesini kullanırtextPathPaint
. Bu dikdörtgenin orta koordinatlarının negatifleri, metni yola dönüştürmek için yöntemine geçirilir GetTextPath
. Sonuçta elde edilen yol, karakterin ortasında (0, 0) noktasına sahiptir ve bu da 1B yol efekti için idealdir.
Oluşturucunun SKPathEffect
sonunda oluşturulan nesnenin alan olarak kaydedilmek yerine özelliğine PathEffect
ayarlanabileceğini textPaint
düşünebilirsiniz. Ancak bu, işleyicideki PaintSurface
çağrının MeasureText
sonuçlarını çarpıttığı için çok iyi çalışmadığı ortaya çıktı:
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);
}
}
Bu MeasureText
çağrı, sayfadaki karakteri ortalamak için kullanılır. Sorunlardan kaçınmak için, PathEffect
metin ölçüldükten sonra ancak görüntülenmeden önce özelliği paint nesnesine ayarlanır.
Karakter Ana Hatlarının Ana Hatları
Normalde yöntemi GetFillPath
SKPaint
, en önemlisi vuruş genişliği ve yol efekti olmak üzere boya özelliklerini uygulayarak bir yolu diğerine dönüştürür. Yol efektleri olmadan kullanıldığında, GetFillPath
etkili bir şekilde başka bir yolu ana hatlarıyla gösteren bir yol oluşturur. Bu, Yol Efektleri makalesinin Yol Ana Hatlarına Dokunun sayfasında gösterilmiştir.
Ayrıca, içinden GetTextPath
döndürülen yolu da çağırabilirsinizGetFillPath
, ancak ilk başta bunun nasıl görüneceğinden tam olarak emin olmayabilirsiniz.
Karakter Ana Hatları sayfası tekniği gösterir. Tüm ilgili kod sınıfının işleyicisindedir PaintSurface
CharacterOutlineOutlinesPage
.
Oluşturucu, sayfanın boyutuna göre bir özellik ile adlı textPaint
bir TextSize
nesne oluşturarak SKPaint
başlar. Bu yöntem kullanılarak GetTextPath
bir yola dönüştürülür. Yolu ekranda etkili bir şekilde ortalamak için GetTextPath
koordinat bağımsız değişkenleri:
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);
}
}
}
}
}
İşleyici PaintSurface
daha sonra adlı outlinePath
yeni bir yol oluşturur. Bu, çağrısında GetFillPath
hedef yol olur. StrokeWidth
25 özelliğinin, metin karakterlerini tuşlayan 25 piksel genişliğinde bir yolun ana hattını tanımlamasına neden oluroutlinePath
. Bu yol daha sonra 5 vuruş genişliğine sahip kırmızı olarak görüntülenir:
Yakından baktığınızda, yol ana hattının keskin bir köşe yaptığı örtüşmeler görürsünüz. Bunlar bu işlemin normal yapıtlarıdır.
Yol Boyunca Metin
Metin normalde yatay taban çizgisinde görüntülenir. Metin dikey veya çapraz çalışacak şekilde döndürülebilir, ancak taban çizgisi hala düz bir çizgidir.
Ancak, metnin eğri boyunca çalışmasını istediğiniz zamanlar vardır. yönteminin DrawTextOnPath
SKCanvas
amacı budur:
public Void DrawTextOnPath (String text, SKPath path, Single hOffset, Single vOffset, SKPaint paint)
İlk bağımsız değişkende belirtilen metin, ikinci bağımsız değişken olarak belirtilen yol boyunca çalışacak şekilde yapılır. Metni, bağımsız değişkeniyle yolun başından itibaren bir uzaklıkta hOffset
başlatabilirsiniz. Normalde yol metnin taban çizgisini oluşturur: Metin artanları yolun bir tarafında, metin alt öğeleri ise diğer tarafındadır. Ancak, bağımsız değişkenle vOffset
metin taban çizgisini yoldan kaydırabilirsiniz.
Bu yöntemin, özelliğini ayarlayarak metnin yolun başından sonuna kadar çalışacak şekilde mükemmel boyutlandırılacak şekilde ayarlanması TextSize
SKPaint
konusunda rehberlik sağlayacak bir özelliği yoktur. Bazen bu metin boyutunu kendiniz anlayabilirsiniz. Diğer zamanlarda Yol Bilgileri ve Numaralandırma ile ilgili bir sonraki makalede açıklanacak yol ölçme işlevlerini kullanmanız gerekir.
Dairesel Metin programı, metni dairenin çevresinde kaydırıyor. Dairenin çevresini belirlemek kolaydır, bu nedenle metni tam olarak sığacak şekilde boyutlandırmak kolaydır. PaintSurface
sınıfının işleyicisiCircularTextPage
, sayfanın boyutuna göre bir dairenin yarıçapını hesaplar. Bu daire olur 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);
}
}
}
}
Özelliği TextSize
textPaint
daha sonra metin genişliğinin dairenin çevresi ile eşleşmesi için ayarlanır:
Metnin kendisi de biraz dairesel olacak şekilde seçilmiştir: "daire" sözcüğü hem cümlenin konusu hem de edat tümceciği nesnesidir.