文章目录
SKCanvas方法
DrawColor 填充颜色
public void DrawColor (SkiaSharp.SKColor color, SkiaSharp.SKBlendMode mode = SkiaSharp.SKBlendMode.Src);
public void DrawColor (SkiaSharp.SKColorF color, SkiaSharp.SKBlendMode mode = SkiaSharp.SKBlendMode.Src);
使用指定颜色和混合方式填充当前裁切区域。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
using (var paint = new SKPaint())
{
canvas.DrawColor(SKColors.LightGreen, SKBlendMode.Src);
canvas.ClipRect(new SKRect(100, 100, 300, 200));
//将颜色填充当前裁切区域
canvas.DrawColor(SKColors.Blue, SKBlendMode.SrcOver);
paint.Color = SKColors.Red;
paint.TextSize = 24;
canvas.DrawText($"DrawColor", 95, 150, paint);
}
使用亮绿填充整个区域,修改裁切区域后,再次填充为蓝色。
DrawDrawable 绘制封装对象
public void DrawDrawable (SkiaSharp.SKDrawable drawable, float x, float y);
public void DrawDrawable (SkiaSharp.SKDrawable drawable, SkiaSharp.SKPoint p);
public void DrawDrawable (SkiaSharp.SKDrawable drawable, ref SkiaSharp.SKMatrix matrix);
绘制一个封装了绘制业务的对象。
- 封装一个绘制笑脸的业务逻辑。
/// <summary>
/// 封闭一个绘制笑脸对象
/// </summary>
public class FaceDrawable:SKDrawable
{
protected override void OnDraw(SKCanvas canvas)
{
using (var paint = new SKPaint())
{
paint.Color = SKColors.LightGreen;
canvas.DrawCircle(100, 100, 100, paint);
paint.Color = SKColors.Red;
paint.IsStroke = true;
canvas.DrawCircle(60,80,25, paint);
canvas.DrawCircle(140, 80, 25, paint);
canvas.DrawArc(new SKRect(75, 90, 125, 130), 45, 90, false, paint);
}
}
}
- 重复横、列多次绘制。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
var drawable = new FaceDrawable();
using (var paint = new SKPaint())
{
//多次绘制一组相同的对象
var bounds=drawable.Bounds;
for (float x = 0; x < this.Width - bounds.Width; x += bounds.Width)
{
for(float y = 0; y < this.Height - bounds.Height; y += bounds.Height)
{
canvas.DrawDrawable(drawable,x,y);
}
}
}
DrawImage 高性能绘制图像
public void DrawImage (SkiaSharp.SKImage image, SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint = default);
public void DrawImage (SkiaSharp.SKImage image, SkiaSharp.SKRect dest, SkiaSharp.SKPaint paint = default);
public void DrawImage (SkiaSharp.SKImage image, SkiaSharp.SKRect source, SkiaSharp.SKRect dest, SkiaSharp.SKPaint paint = default);
public void DrawImage (SkiaSharp.SKImage image, float x, float y, SkiaSharp.SKPaint paint = default);
相比SKBitmap,性能更优,但SKImage内容不可变。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
if (skBmp == null)
{
skBmp = SKBitmap.Decode(@"Images\AIWoman.png");
skBmp = skBmp.Resize(new SKSizeI(350, 350), SKFilterQuality.High);
}
if (skImg == null) skImg = SKImage.FromBitmap(skBmp);
const int DrawCount = 1000;
using (var paint = new SKPaint())
{
paint.IsAntialias = true;
paint.FilterQuality = SKFilterQuality.High;
paint.TextSize = 24;
for (int i = 0; i < 2; i++)
{
count += 1;
var sw = Stopwatch.StartNew();
for (int j = 0; j < DrawCount; j++)
{
if (count % 2 == 0)
{
canvas.DrawImage(skImg, 400, 20, paint);
}
else
{
canvas.DrawBitmap(skBmp, 0, 20, paint);
}
}
sw.Stop();
if (count % 2 == 0)
{
canvas.DrawText($"DrawImage {DrawCount}times:{sw.ElapsedMilliseconds}ms", 400, 400, paint);
}
else
{
canvas.DrawText($"DrawBitmap {DrawCount}times:{sw.ElapsedMilliseconds}ms", 0, 400, paint);
}
}
}
对比多次使用SKBitmap与SKImage绘制同一幅图像。
SKBitmap与SKImage对比
SKBitmap
-
可变性:
SKBitmap
是可变的,允许对像素进行直接的读写操作。这使得它非常适合需要频繁修改图像内容的情况。 -
内存管理:
SKBitmap
的内存是可管理的,这意味着你可以控制它的分配和释放。开发者需要注意内存管理以防止内存泄漏。 -
使用场景:
适用于需要直接处理像素数据的场景,如图像编辑、动态生成图像等。
SKImage
-
不可变性:
SKImage
是不可变的,一旦创建后其内容无法更改。这使得它非常适合用于高效的图像显示和传递。 -
内存管理:
SKImage
通常是通过图形硬件加速的,因此在性能上优于SKBitmap
。内存管理更为自动化,不需要开发者显式地管理。 -
使用场景:
适用于图像渲染、显示和传输,如绘制在屏幕上、保存为文件等。
DrawPicture 绘制图像
public void DrawPicture (SkiaSharp.SKPicture picture, SkiaSharp.SKPaint paint = default);
public void DrawPicture (SkiaSharp.SKPicture picture, ref SkiaSharp.SKMatrix matrix, SkiaSharp.SKPaint paint = default);
public void DrawPicture (SkiaSharp.SKPicture picture, SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint = default);
public void DrawPicture (SkiaSharp.SKPicture picture, float x, float y, SkiaSharp.SKPaint paint = default);
SKPicture
主要用于记录绘图命令以便在以后重用。
-
高效的重绘:
SKPicture
可以记录一次绘图操作的所有命令,然后在需要的时候进行重放。这样可以避免每次重绘时重新执行复杂的绘图逻辑,提高绘图的效率。
-
减少CPU开销:
- 在一些复杂的绘图操作中,通过记录绘图命令并重放,可以减少CPU的计算开销,因为重放绘图命令比重新执行绘图逻辑所需的计算量更小。
-
方便的图形内容重用:
SKPicture
允许将绘图命令记录下来并在不同的地方重用。例如,可以在多个视图中重用相同的图形内容,而不需要每次都重新绘制。
-
支持多线程绘图:
SKPicture
的记录和重放机制使得绘图命令可以在后台线程中生成,然后在主线程中重放。这有助于实现更流畅的用户界面和更好的响应性。
-
简化复杂场景的处理:
- 对于一些复杂的场景绘制,可以先将场景绘制到
SKPicture
中,然后在需要的时候重放这个SKPicture
。这使得处理复杂场景变得更加简单和直观。
- 对于一些复杂的场景绘制,可以先将场景绘制到
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
if (skPic == null)
{
using (var recorder = new SKPictureRecorder())
{
using (var picCanvas = recorder.BeginRecording(SKRect.Create(400, 400)))
using (var paint = new SKPaint())
{
paint.Color = SKColors.Red;
paint.IsStroke = true;
picCanvas.DrawCircle(60, 60, 50, paint);
paint.IsStroke = false;
paint.Color = SKColors.LightGreen;
picCanvas.DrawRoundRect(100, 100, 350, 350, 50, 50, paint);
}
skPic = recorder.EndRecording();
}
}
using(var paint=new SKPaint())
{
canvas.DrawPicture(skPic, paint);
canvas.Translate(350, 350);
canvas.DrawPicture(skPic, paint);
}
使用SKPictureRecorder生成SKPicture。
DrawPoint / DrawPoints 绘制点
public void DrawPoint (SkiaSharp.SKPoint p, SkiaSharp.SKColor color);
public void DrawPoint (SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint);
public void DrawPoint (float x, float y, SkiaSharp.SKColor color);
public void DrawPoint (float x, float y, SkiaSharp.SKPaint paint);
public void DrawPoints (SkiaSharp.SKPointMode mode, SkiaSharp.SKPoint[] points, SkiaSharp.SKPaint paint);
绘制点和点集
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
using (var paint = new SKPaint())
{
canvas.DrawPoint(50,50,SKColors.Red);
canvas.DrawPoint(100, 50, SKColors.Blue);
paint.StrokeWidth = 10;
paint.Color = SKColors.Red;
canvas.DrawPoint(50, 100, paint);
paint.Color = SKColors.Blue;
paint.StrokeCap = SKStrokeCap.Round;
canvas.DrawPoint(100, 100, paint);
var points = new SKPoint[] {
new SKPoint(200,20),
new SKPoint(400,20),
new SKPoint(400,120),
new SKPoint(200,120),
new SKPoint(200,160),
new SKPoint(300,160)
};
canvas.DrawPoints(SKPointMode.Points, points, paint);
paint.Color = SKColors.Green;
canvas.Translate(0, 200);
canvas.DrawPoints(SKPointMode.Lines, points, paint);
paint.Color = SKColors.Red;
canvas.Translate(0, 200);
canvas.DrawPoints(SKPointMode.Polygon, points, paint);
}
可以绘制点集、线段(两个点连一条线),多段线
DrawRoundRect/DrawRoundRectDifference绘制圆角矩形
public void DrawRoundRect (SkiaSharp.SKRoundRect rect, SkiaSharp.SKPaint paint);
public void DrawRoundRect (SkiaSharp.SKRect rect, SkiaSharp.SKSize r, SkiaSharp.SKPaint paint);
public void DrawRoundRect (SkiaSharp.SKRect rect, float rx, float ry, SkiaSharp.SKPaint paint);
public void DrawRoundRect (float x, float y, float w, float h, float rx, float ry, SkiaSharp.SKPaint paint);
public void DrawRoundRectDifference (SkiaSharp.SKRoundRect outer, SkiaSharp.SKRoundRect inner, SkiaSharp.SKPaint paint);
绘制圆角矩形及两个圆角矩形之间的区域。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
using (var paint = new SKPaint())
using (var txtPaint = new SKPaint())
{
txtPaint.IsAntialias = true;
txtPaint.Color = SKColors.Red;
txtPaint.TextSize = 18;
paint.IsAntialias = true;
paint.IsStroke = true;
paint.StrokeWidth = 2;
var rect = new SKRect(50, 50, 250, 150);
canvas.DrawRoundRect(rect, new SKSize(20, 20), paint);
canvas.DrawText($"DrawRoundRect, rx=20,ry=20", 50, 180, txtPaint);
paint.Color = SKColors.Red;
canvas.Translate(250, 0);
canvas.DrawRoundRect(rect, new SKSize(50, 20), paint);
canvas.DrawText($"DrawRoundRect, rx=50,ry=20", 50, 180, txtPaint);
paint.Style = SKPaintStyle.Fill;
canvas.Translate(0, 100);
// 定义外部圆角矩形
var outerRect = new SKRoundRect(new SKRect(100, 100, 500, 400), 50, 50);
// 定义内部圆角矩形
var innerRect = new SKRoundRect(new SKRect(120, 120, 480, 380), 30, 50);
canvas.DrawRoundRectDifference(outerRect, innerRect, paint);
}
绘制不同圆角的圆角矩形,及两个圆角矩形之间区域。
DrawSurface 绘制Surface
public void DrawSurface (SkiaSharp.SKSurface surface, SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint = default);
public void DrawSurface (SkiaSharp.SKSurface surface, float x, float y, SkiaSharp.SKPaint paint = default);
将Surface绘制到当前画布上。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
using (var paint = new SKPaint())
{
using (var skBmp = new SKBitmap(400, 400))
using (var surfaceA = SKSurface.Create(skBmp.Info, skBmp.RowBytes))
using (var canvasA = surfaceA.Canvas)
using (var paintA = new SKPaint())
{
paintA.IsAntialias = true;
paintA.Color = SKColors.Red;
paintA.IsStroke = true;
canvasA.Clear(SKColors.White);
canvasA.DrawRect(20, 20, 300, 120, paintA);
canvasA.DrawCircle(200, 200, 100, paintA);
canvas.DrawSurface(surfaceA, 50, 50);
canvas.DrawText($"DrawSurface", 50, 350, paint);
canvas.DrawSurface(surfaceA, 350, 350);
canvas.DrawText($"DrawSurface", 350,650, paint);
}
}
DrawTextOnPath沿路径绘制文字
public void DrawTextOnPath (string text, SkiaSharp.SKPath path, SkiaSharp.SKPoint offset, bool warpGlyphs, SkiaSharp.SKPaint paint);
public void DrawTextOnPath (string text, SkiaSharp.SKPath path, SkiaSharp.SKPoint offset, SkiaSharp.SKPaint paint);
public void DrawTextOnPath (string text, SkiaSharp.SKPath path, float hOffset, float vOffset, SkiaSharp.SKPaint paint);
定义一个路径,可使文字基线沿着路径还不是普通的直线。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
string text = $"SkiaSharp可以直接绘制一段绕着路径(如曲线)行走的文字,是不是很Cool!";
var path = new SKPath();
path.MoveTo(50, 50); // 起始点
path.CubicTo(250, 400, 550, 100, 750, 600); // 三次贝塞尔曲线
var pathMeasure = new SKPathMeasure(path, false);
using (var paint = new SKPaint())
{
paint.IsAntialias = true;
paint.Color = SKColors.Red;
paint.TextSize = 22;
if (timer == null)
{
var pathLength = pathMeasure.Length / 2;
hOffset = pathLength;
timer = new System.Windows.Forms.Timer();
timer.Interval = 30;
timer.Tick += (o, t) =>
{
hOffset -= 2;
if (hOffset <= -pathLength)
{
hOffset = pathLength;
}
this.TNTechImageBox.Invalidate();
};
timer.Start();
}
paint.Typeface = SKTypeface.FromFamilyName("宋体");
var vOffset = -paint.TextSize / 2; // 垂直偏移量,使文本中心对齐路径
canvas.DrawTextOnPath(text, path, hOffset, vOffset, paint);
paint.IsStroke = true;
canvas.DrawPath(path, paint);
}
- 定义一段贝赛尔曲线
- 定义一段文字
- 定义一个定时器
- 使文字沿着曲线动态绘制。