【SkiaSharp绘图12】SKCanvas方法详解(一)清空、裁切区域设置、连接矩阵、注释、弧与扇形、图集、九宫格绘图、圆

发布于:2024-07-04 ⋅ 阅读:(164) ⋅ 点赞:(0)

SKCanvas 方法

Clear 清空

public void Clear ();//清空为#00000000
public void Clear (SkiaSharp.SKColor color);
public void Clear (SkiaSharp.SKColorF color);

用指定颜色替换当前裁切区域的所有像素颜色。

ClipPath/ClipRect/ClipRegion/ClipRoundRect 设置裁切区域

public void ClipPath (SkiaSharp.SKPath path, SkiaSharp.SKClipOperation operation = SkiaSharp.SKClipOperation.Intersect, bool antialias = false);
public void ClipRect (SkiaSharp.SKRect rect, SkiaSharp.SKClipOperation operation = SkiaSharp.SKClipOperation.Intersect, bool antialias = false);
public void ClipRegion (SkiaSharp.SKRegion region, SkiaSharp.SKClipOperation operation = SkiaSharp.SKClipOperation.Intersect);
public void ClipRegion (SkiaSharp.SKRegion region, SkiaSharp.SKClipOperation operation = SkiaSharp.SKClipOperation.Intersect);

使用指定的路径修改当前裁切区域。
SKClipOperation

说明
Difference 从原区域减去指定区域
Intersect 原区域与指定区域的交集
var canvas = e.Surface.Canvas;
var info = e.Info;

using(var paint=new SKPaint())
{
    paint.TextSize = 18;
    paint.Color = SKColors.Red;
    var path = new SKPath();
    path.AddCircle(200, 200, 100);

    canvas.Save();
    canvas.ClipPath(path);
    canvas.Clear(SKColors.LightGreen);
    canvas.DrawText($"ClipPath", 150, 200, paint);

    canvas.Restore();
    canvas.Save();
    canvas.ClipRect(new SKRect(250, 100, 550, 300));
    canvas.Clear(SKColors.LightBlue);
    canvas.DrawText($"ClipRect", 350, 200, paint);

    canvas.Restore();
    canvas.Save();
    canvas.ClipRegion(new SKRegion(new SKRectI(100, 280, 300, 450)));
    canvas.Clear(SKColors.LightPink);
    canvas.DrawText($"ClipRegion", 120, 400, paint);

    canvas.Restore();
    canvas.ClipRoundRect(new SKRoundRect(new SKRect(350, 350, 600, 600), 50));
    canvas.Clear(SKColors.LightCyan);
    canvas.DrawText($"ClipRoundRect", 400, 450, paint);
}

分配使用ClipPath、ClipRect、ClipRegion、ClipRoundRect修改裁切区域。
注意,还原裁切区域需要使用SKCanvas的Save()与Restore()方法。
裁切区域

Concat 连接矩阵

public void Concat (ref SkiaSharp.SKMatrix m);

原SKCanvas的矩阵与指定合并。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);
using (var paint = new SKPaint())
{
    paint.TextSize = 18;
    paint.Color = SKColors.LightGreen;
    paint.IsStroke = true;
    var rect = new SKRect(50, 50, 250, 150);
    canvas.DrawRect(rect, paint);
    canvas.DrawText($"TotalMatrix:{string.Join(",", canvas.TotalMatrix.Values)}", 400, 100, paint);

    var matrixA = SKMatrix.CreateTranslation(50, 50);
    canvas.Concat(ref matrixA);
    canvas.DrawRect(rect, paint);
    canvas.DrawText($"TotalMatrix:{string.Join(",", canvas.TotalMatrix.Values)}", 400, 100, paint);

    canvas.Concat(ref matrixA);
    canvas.DrawRect(rect, paint);
    canvas.DrawText($"TotalMatrix:{string.Join(",", canvas.TotalMatrix.Values)}", 400, 100, paint);
}

Concat

DrawAnnotation绘制注释

public void DrawAnnotation (SkiaSharp.SKRect rect, string key, SkiaSharp.SKData value);

绘制注释。(只在某些SKCanvas中有效,如PDF doc生成的SKCanvas)
实测使用CreatePdf也没成功。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

using (var stream = File.OpenWrite(@"Images\test.pdf"))
using (var doc = SKDocument.CreatePdf(stream,72))
using (var pdfCanvas = doc.BeginPage(600, 600))
using (var paint = new SKPaint())
{
    paint.TextSize = 18;
    paint.Color = SKColors.LightGreen;
    paint.IsStroke = true;
    var rect = new SKRect(50, 50, 250, 150);
    pdfCanvas.DrawRect(rect, paint);

    using (MemoryStream byteStream = new MemoryStream(Encoding.UTF8.GetBytes("This is rect annotation")))
    {
        // 重置流的位置
        byteStream.Position = 0;
        using (var annoData = SKData.Create(byteStream))
        {
            //生成的PDF没看到
            pdfCanvas.DrawAnnotation(rect, "Rect Anno", annoData);
        }
    }
    pdfCanvas.DrawText($"Test DrawAnnotation", 20, 200, paint);
    doc.EndPage();
    doc.Close();
}

DrawArc绘制椭圆弧、扇形

public void DrawArc (SkiaSharp.SKRect oval, float startAngle, float sweepAngle, bool useCenter, SkiaSharp.SKPaint paint);
参数 说明
oval 确定弧所在椭圆的矩形
startAngle 起始角度
sweepAngle 持续角度(正数:顺时针,负数:逆时针)
useCenter true:扇形,false:弧
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

using (var paint = new SKPaint())
{
    paint.TextSize = 18;
    paint.Color = SKColors.Red;
    paint.IsStroke = true;
    paint.StrokeWidth = 5;
    var rect = new SKRect(50, 50, 250, 150);
    //顺时针120度
    canvas.DrawArc(rect, 0, 120, false, paint);

    paint.Color = SKColors.Green;
    //逆时针120度
    canvas.DrawArc(rect, 0, -120, false, paint);

    paint.Color = SKColors.Blue;
    //扇形
    canvas.DrawArc(rect, 120, 120, true, paint);
}

分三段绘制,其中一段为扇形。
DrawArc

DrawAtlas绘制图集(一个图像、多个区域、多个缩放、一次绘制)

public void DrawAtlas (SkiaSharp.SKImage atlas, SkiaSharp.SKRect[] sprites, SkiaSharp.SKRotationScaleMatrix[] transforms, SkiaSharp.SKPaint paint);
public void DrawAtlas (SkiaSharp.SKImage atlas, SkiaSharp.SKRect[] sprites, SkiaSharp.SKRotationScaleMatrix[] transforms, SkiaSharp.SKColor[] colors, SkiaSharp.SKBlendMode mode, SkiaSharp.SKPaint paint);
public void DrawAtlas (SkiaSharp.SKImage atlas, SkiaSharp.SKRect[] sprites, SkiaSharp.SKRotationScaleMatrix[] transforms, SkiaSharp.SKColor[] colors, SkiaSharp.SKBlendMode mode, SkiaSharp.SKRect cullRect, SkiaSharp.SKPaint paint);

通过指定多个原图像矩形区域和多个缩放矩阵,将一张图像一次性绘制在画布上。

  1. 提高性能:减少绘制调用的次数可以显著提高渲染性能,尤其是在需要同时绘制大量小图像时。
  2. 降低内存开销:使用一个大图集而不是多个小图像可以减少纹理切换次数,降低 GPU 内存开销。
  3. 简化管理:将多个图像集中在一个图集中便于管理和使用,特别是在游戏开发或需要大量图形资源的应用中。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

using (var paint = new SKPaint())
{
    paint.Color = SKColors.Red;

    if (skImg == null) skImg = SKImage.FromBitmap(SKBitmap.Decode(@"Images\wall.png"));

    const int count = 100;
    var srcRects = new List<SKRect>();
    var rotScaleMatrix = new List<SKRotationScaleMatrix>();
    var offsetY = 0;
    var offsetX = 0;
    for (int i = 0; i < count; i++)
    {
        srcRects.Add(SKRect.Create(skImg.Width, skImg.Height));
        rotScaleMatrix.Add(SKRotationScaleMatrix.CreateTranslation(offsetX, offsetY));
        offsetX += skImg.Width;
        if (offsetX > info.Width - skImg.Width)
        {
            offsetX = 0;
            offsetY += skImg.Height;
        }
    }
    //模拟平铺
    canvas.DrawAtlas(skImg,srcRects.ToArray(), rotScaleMatrix.ToArray(), paint);
}

模拟平铺的方式,多次绘制同一张图像。
DrawAtlas

DrawBitmap绘制图像

public void DrawBitmap (SkiaSharp.SKBitmap bitmap, SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint = default);
public void DrawBitmap (SkiaSharp.SKBitmap bitmap, SkiaSharp.SKRect dest, SkiaSharp.SKPaint paint = default);
public void DrawBitmap (SkiaSharp.SKBitmap bitmap, SkiaSharp.SKRect source, SkiaSharp.SKRect dest, SkiaSharp.SKPaint paint = default);
public void DrawBitmap (SkiaSharp.SKBitmap bitmap, float x, float y, SkiaSharp.SKPaint paint = default);

将图像绘制到指定区域。(注意,先缩放或裁切到合适大小再绘制,比直接绘制时缩放,效率会高些)。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

using (var paint = new SKPaint())
{
    paint.Color = SKColors.Red;

    if (skBmp == null)
    {
        skBmp = SKBitmap.Decode(@"Images\AIWoman.png");
        skBmp = skBmp.Resize(new SKSizeI(400, 400), SKFilterQuality.High);
    }

    canvas.DrawBitmap(skBmp, new SKPoint(20, 20), paint);

    canvas.DrawBitmap(skBmp, new SKRect(440, 20, 440 + 300, 20 + 300), paint);

    var srcRect = new SKRect(40, 40, 360, 360);

    canvas.DrawBitmap(skBmp, srcRect, new SKRect(20, 440, 20 + srcRect.Width, 480 + srcRect.Height), paint);
}

DrawBitmap

DrawBitmapNinePatch九宫格绘图

public void DrawBitmapNinePatch (SkiaSharp.SKBitmap bitmap, SkiaSharp.SKRectI center, SkiaSharp.SKRect dst, SkiaSharp.SKPaint paint = default);

以九宫格形式绘制一幅图像,通过指定中心矩形来局部缩放图像。九宫格中,四个角保持不拉伸,其中的随着目标矩形大小拉伸。

 /// <summary>
 /// 绘制刻度和根据中心矩形绘制井字形
 /// </summary>
 /// <param name="bmp"></param>
 /// <param name="centerRectI"></param>
 /// <returns></returns>
 private SKBitmap DrawLines(SKBitmap bmp,SKRectI centerRectI)
 {
     var cloneBmp = bmp.Copy();
     using (var paint = new SKPaint())
     using (var sCanvas = new SKCanvas(cloneBmp))
     {
         paint.Color = SKColors.Red;
         paint.IsStroke = true;
         for (var x = 0; x < cloneBmp.Width; x += 10)
         {
             sCanvas.DrawLine(x, 0, x, 10, paint);
         }
         sCanvas.DrawLine(0, 10, cloneBmp.Width, 10, paint);

         for (var y = 0; y < cloneBmp.Height; y += 10)
         {
             sCanvas.DrawLine(0, y, 10, y, paint);
         }
         sCanvas.DrawLine(10, 0, 10, cloneBmp.Height, paint);

         sCanvas.DrawLine(0, centerRectI.Top, cloneBmp.Width, centerRectI.Top, paint);
         sCanvas.DrawLine(0, centerRectI.Bottom, cloneBmp.Width, centerRectI.Bottom, paint);

         sCanvas.DrawLine(centerRectI.Left, 0, centerRectI.Left, cloneBmp.Height, paint);
         sCanvas.DrawLine(centerRectI.Right, 0, centerRectI.Right, cloneBmp.Height, paint);
     }
     return cloneBmp;
 }
public void OnPaintSurface12_07(object sender, SkiaSharp.Views.Desktop.SKPaintGLSurfaceEventArgs e)
{
    var canvas = e.Surface.Canvas;
    var info = e.Info;
    canvas.Clear(SKColors.White);

    //中间矩形
    SKRectI centerRectI = SKRectI.Empty;

    // 定义绘制目标矩形
    var dstRect = new SKRect(0, 0, 400, 400);

    using (var paint = new SKPaint())
    {
        paint.Color = SKColors.Red;
        paint.TextSize = 16;

        var skSizeI = new SKSizeI(200, 200);
        centerRectI = new SKRectI(10, 10, 60, 60);
        if (skBmp == null)
        {
            skBmp = SKBitmap.Decode(@"Images\AIWoman.png");
            skBmp = skBmp.Resize(new SKSizeI(200, 200), SKFilterQuality.High);                    
        }                
        var bmpA= DrawLines(skBmp, centerRectI);
        canvas.DrawBitmapNinePatch(bmpA, centerRectI, dstRect, paint);
        canvas.DrawText($"Center Rect:{centerRectI}", 0, 430, paint);
        bmpA.Dispose();

        centerRectI =new SKRectI(skBmp.Width/3,skBmp.Height/3,skBmp.Width*2/3,skBmp.Height*2/3);
        var bmpB= DrawLines(skBmp, centerRectI);

       

        dstRect = new SKRect(410, 0, 810, 400);
        canvas.DrawBitmapNinePatch(bmpB, centerRectI, dstRect, paint);
        canvas.DrawText($"Center Rect:{centerRectI}", 410, 430, paint);
        bmpB.Dispose();

    }
}

定义两个不同的中心矩形,对同一幅图进行九宫格缩放,观察它们之间的不同。
DrawBitmapNinePatch

DrawBitmapLattice 格子绘图

public void DrawBitmapLattice (SkiaSharp.SKBitmap bitmap, SkiaSharp.SKLattice lattice, SkiaSharp.SKRect dst, SkiaSharp.SKPaint paint = default);
public void DrawBitmapLattice (SkiaSharp.SKBitmap bitmap, int[] xDivs, int[] yDivs, SkiaSharp.SKRect dst, SkiaSharp.SKPaint paint = default);

按函数原型是对九宫格绘图的扩展,可自定义长*宽个格子,也可指定哪一排或哪一列的缩放、以及哪一格是透明还是颜色填色。但实际应用中,相关的参数并不一定按实际指定的效果执行。

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(200, 200), SKFilterQuality.High);
}

var qtrWidth = skBmp.Width / 4;
// 定义 Lattice
var lattice = new SKLattice
{
    XDivs = new int[] { qtrWidth, qtrWidth * 2, qtrWidth * 3 }, // X 方向上的分割位置
    YDivs = new int[] { skBmp.Height / 3, 2 * skBmp.Height / 3 }, // Y 方向上的分割位置
    RectTypes = new SKLatticeRectType[] // 定义每个网格的类型
    {
        SKLatticeRectType.FixedColor, SKLatticeRectType.Default, SKLatticeRectType.Default,SKLatticeRectType.FixedColor,
        SKLatticeRectType.FixedColor, SKLatticeRectType.Default, SKLatticeRectType.Default,SKLatticeRectType.FixedColor,
        SKLatticeRectType.FixedColor, SKLatticeRectType.Default, SKLatticeRectType.Default,SKLatticeRectType.FixedColor
    },
    Colors = new SKColor[]
                {
                    SKColors.Red,     
                    SKColors.Green,   
                    SKColors.Blue,    
                    SKColors.Yellow,  
                    SKColors.Cyan,    
                    SKColors.Magenta, 
                    SKColors.Orange,  
                    SKColors.Purple,  
                    SKColors.Gray,
                    SKColors.Pink,
                    SKColors.DarkBlue,
                    SKColors.Gold
                }
};



// 定义绘制目标矩形
var dstRect = new SKRect(0, 0, 300, 300);

using (var paint = new SKPaint())
{
    paint.Color = SKColors.Red;

    using (var sCanvas = new SKCanvas(skBmp))
    {
        foreach (var x in lattice.XDivs)
        {
            sCanvas.DrawLine(x, 0, x, skBmp.Height, paint);
        }
        foreach (var y in lattice.YDivs)
        {
            sCanvas.DrawLine(0, y, skBmp.Width, y, paint);
        }
    }
    canvas.DrawBitmapLattice(skBmp, lattice, dstRect, paint);
}

按 4 * 3 格子对图像进行缩放绘制
在这里插入图片描述

DrawCircle 绘制圆

public void DrawCircle (SkiaSharp.SKPoint c, float radius, SkiaSharp.SKPaint paint);
public void DrawCircle (float cx, float cy, float radius, SkiaSharp.SKPaint paint);

绘制和填充圆。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

using(var paint=new SKPaint())
{
    paint.Style = SKPaintStyle.StrokeAndFill;
    paint.Color = SKColors.LightGreen;
    paint.TextSize = 18;

    //实心圆
    canvas.DrawCircle(150, 150, 100, paint);
    canvas.DrawText($"Fill Circle", 50, 280, paint);


    canvas.Translate(200, 0);
    paint.IsStroke = true;
    //空心圆
    canvas.DrawCircle(150, 150, 100, paint);
    canvas.DrawText($"Stroke Circle", 50, 280, paint);
}

绘制两个圆,一个填充,另一个只描边。
DrawCircle


网站公告

今日签到

点亮在社区的每一天
去签到