如何使用SkiaSharp在WPF的WriteableBitmap上绘制文本
引言
在图像处理和图形渲染的世界里,SkiaSharp和WPF都是不可或缺的工具。然而,当需要在WPF的WriteableBitmap
上绘制文本或图形时,如何优雅地结合这两个工具呢?在这篇文章中,我们将介绍一个简单的扩展方法,它允许你在WriteableBitmap
上使用SkiaSharp进行文本绘制。
基础准备
首先,确保你已经安装了SkiaSharp的NuGet包。
Install-Package SkiaSharp -Version 2.80.3
创建一个扩展方法
我们首先创建一个WriteableBitmapExtensions
类来存放我们的扩展方法。
public static class WriteableBitmapExtensions
{
// 扩展方法将在这里定义
}
添加对齐枚举
在WPF中,我们通常使用TextAlignment
和VerticalAlignment
来设置对齐方式。然而,在SkiaSharp中,这些需要手动设置。为了解决这个问题,我们可以定义自己的枚举类型。
public enum TextAlignment
{
Left,
Center,
Right
}
public enum VerticalAlignment
{
Top,
Center,
Bottom
}
绘制文本的扩展方法
接下来,我们定义DrawText
方法,该方法接受一个WriteableBitmap
实例、文本、字体和各种其他参数。
具体的代码:
// File: WriteableBitmapExtensions
// Author: linxmouse@gmail.com
// Creation: 2023/10/17 13:55:49
using System;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows;
using SkiaSharp;
namespace WritableBitmapDemo
{
public enum TextAlignment
{
Left,
Center,
Right
}
public enum VerticalAlignment
{
Top,
Center,
Bottom
}
public static class WriteableBitmapExtensions
{
public static void DrawText(this WriteableBitmap writeableBitmap, string text, Point position, Typeface typeface, double fontSize, Brush brush,
TextAlignment horizontalAlign = TextAlignment.Left, VerticalAlignment verticalAlign = VerticalAlignment.Top)
{
// 锁定 WriteableBitmap 的后备缓冲区
writeableBitmap.Lock();
// 获取后备缓冲区的信息
IntPtr buffer = writeableBitmap.BackBuffer;
int width = writeableBitmap.PixelWidth;
int height = writeableBitmap.PixelHeight;
int stride = writeableBitmap.BackBufferStride;
// 创建 Skia 的图像信息对象
SKImageInfo imageInfo = new SKImageInfo(width, height, SKColorType.Bgra8888);
// 创建 Skia 的绘图表面
using (SKBitmap bitmap = new SKBitmap(imageInfo))
{
bitmap.InstallPixels(imageInfo, buffer, stride);
using (SKCanvas canvas = new SKCanvas(bitmap))
{
// 绘制文本或其他图形元素
using (SKPaint paint = new SKPaint())
{
paint.TextSize = (float)fontSize;
paint.IsAntialias = true;
// 使用 WPF 的 Typeface 创建 SkiaSharp 的 SKTypeface
if (typeface != null)
{
var fontFamily = typeface.FontFamily.ToString();
var weight = typeface.Weight.ToOpenTypeWeight();
var slant = typeface.Style == FontStyles.Normal ? SKFontStyleSlant.Upright : SKFontStyleSlant.Italic;
var skFontStyle = new SKFontStyle(weight, 5, slant); // 这里的5是宽度参数,通常默认值即可
using (var skTypeface = SKTypeface.FromFamilyName(fontFamily, skFontStyle))
{
paint.Typeface = skTypeface;
}
}
// 根据自定义的对齐方式枚举设置Skia的对齐方式
switch (horizontalAlign)
{
case TextAlignment.Left:
paint.TextAlign = SKTextAlign.Left;
break;
case TextAlignment.Center:
paint.TextAlign = SKTextAlign.Center;
break;
case TextAlignment.Right:
paint.TextAlign = SKTextAlign.Right;
break;
}
// 从WPF的Brush对象中获取颜色信息
var solidColorBrush = brush as SolidColorBrush;
if (solidColorBrush != null)
{
var color = solidColorBrush.Color;
paint.Color = new SKColor(color.R, color.G, color.B, color.A);
}
paint.BlendMode = SKBlendMode.SrcOver;
// 计算文本宽度和高度
float textWidth = paint.MeasureText(text);
float textHeight = paint.FontMetrics.Descent - paint.FontMetrics.Ascent;
float textBaseline = -paint.FontMetrics.Ascent;
// 根据对齐方式调整x, y坐标
float adjustedX = (float)position.X;
float adjustedY = (float)position.Y;
if (horizontalAlign == TextAlignment.Center) adjustedX -= textWidth / 2;
else if (horizontalAlign == TextAlignment.Right) adjustedX -= textWidth;
if (verticalAlign == VerticalAlignment.Center) adjustedY += textHeight / 2 - textBaseline;
else if (verticalAlign == VerticalAlignment.Bottom) adjustedY += textHeight - textBaseline;
else adjustedY += textBaseline;
canvas.DrawText(text, adjustedX, adjustedY, paint);
}
// 将绘制结果复制回 WriteableBitmap 的后备缓冲区
SKPixmap pixmap = bitmap.PeekPixels();
pixmap.ReadPixels(imageInfo, buffer, stride, 0, 0);
}
}
// 解锁 WriteableBitmap 的后备缓冲区并进行刷新
writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
writeableBitmap.Unlock();
}
}
}
使用示例
var writeableBitmap = new WriteableBitmap(200, 200, 96, 96, PixelFormats.Bgra32, null);
var typeface = new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal);
var brush = new SolidColorBrush(Colors.Black);
writeableBitmap.DrawText("Hello, world!", new Point(50, 50), typeface, 12, brush, TextAlignment.Center, VerticalAlignment.Center);