【SkiaSharp绘图13】SKCanvas方法详解(二)填充颜色、封装对象、高性能绘制、点(集)(多段)线、圆角矩形、Surface、沿路径绘制文字

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);
}

使用亮绿填充整个区域,修改裁切区域后,再次填充为蓝色。

DrawColor

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);

绘制一个封装了绘制业务的对象。

  1. 封装一个绘制笑脸的业务逻辑。
/// <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);
        }                
    }
}
  1. 重复横、列多次绘制。
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);
        }
    }
}

DrawDrawable

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绘制同一幅图像。
DrawImage

SKBitmap与SKImage对比

SKBitmap

  1. 可变性
    SKBitmap 是可变的,允许对像素进行直接的读写操作。这使得它非常适合需要频繁修改图像内容的情况。

  2. 内存管理
    SKBitmap 的内存是可管理的,这意味着你可以控制它的分配和释放。开发者需要注意内存管理以防止内存泄漏。

  3. 使用场景
    适用于需要直接处理像素数据的场景,如图像编辑、动态生成图像等。

SKImage

  1. 不可变性
    SKImage 是不可变的,一旦创建后其内容无法更改。这使得它非常适合用于高效的图像显示和传递。

  2. 内存管理
    SKImage 通常是通过图形硬件加速的,因此在性能上优于 SKBitmap。内存管理更为自动化,不需要开发者显式地管理。

  3. 使用场景
    适用于图像渲染、显示和传输,如绘制在屏幕上、保存为文件等。

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

主要用于记录绘图命令以便在以后重用。

  1. 高效的重绘

    • SKPicture 可以记录一次绘图操作的所有命令,然后在需要的时候进行重放。这样可以避免每次重绘时重新执行复杂的绘图逻辑,提高绘图的效率。
  2. 减少CPU开销

    • 在一些复杂的绘图操作中,通过记录绘图命令并重放,可以减少CPU的计算开销,因为重放绘图命令比重新执行绘图逻辑所需的计算量更小。
  3. 方便的图形内容重用

    • SKPicture 允许将绘图命令记录下来并在不同的地方重用。例如,可以在多个视图中重用相同的图形内容,而不需要每次都重新绘制。
  4. 支持多线程绘图

    • SKPicture 的记录和重放机制使得绘图命令可以在后台线程中生成,然后在主线程中重放。这有助于实现更流畅的用户界面和更好的响应性。
  5. 简化复杂场景的处理

    • 对于一些复杂的场景绘制,可以先将场景绘制到 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。
DrawPicture

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);
}

可以绘制点集、线段(两个点连一条线),多段线
DrawPoint/DrawPoints

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);

    }
}

DrawSurface

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);

}
  1. 定义一段贝赛尔曲线
  2. 定义一段文字
  3. 定义一个定时器
  4. 使文字沿着曲线动态绘制。

DrawTextOnPath

  • 13
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图南科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值