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

文章目录

  • SKCanvas方法
    • DrawColor 填充颜色
    • DrawDrawable 绘制封装对象
    • DrawImage 高性能绘制图像
    • SKBitmap与SKImage对比
    • DrawPicture 绘制图像
      • SKPicture
    • DrawPoint / DrawPoints 绘制点
    • DrawRoundRect/DrawRoundRectDifference绘制圆角矩形
    • DrawSurface 绘制Surface
    • DrawTextOnPath沿路径绘制文字

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/37328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

List接口, ArrayList Vector LinkedList

Collection接口的子接口 子类Vector&#xff0c;ArrayList&#xff0c;LinkedList 1.元素的添加顺序和取出顺序一致&#xff0c;且可重复 2.每个元素都有其对应的顺序索引 方法 在index 1 的位置插入一个对象&#xff0c;list.add(1,list2)获取指定index位置的元素&#…

sheng的学习笔记-AI-聚类(Clustering)

ai目录 sheng的学习笔记-AI目录-CSDN博客 基础知识 什么是聚类 在“无监督学习”(unsupervised learning)中&#xff0c;训练样本的标记信息是未知的&#xff0c;目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律&#xff0c;为进一步的数据分析提供基础。此类学…

Android跨进程通信,binder传输数据过大导致客户端APP,Crash,异常捕获,监听异常的数值临界值,提前Hook拦截。

文章目录 Android跨进程通信&#xff0c;binder传输数据过大导致Crash&#xff0c;异常捕获&#xff0c;监听异常的数值临界值&#xff0c;提前Hook拦截。1.binder在做跨进程传输时&#xff0c;最大可以携带多少数据1.1有时候这个1m的崩溃系统捕获不到异常&#xff0c; 2.监测异…

志愿填报指南:为什么我强烈建议你报考计算机专业

首先恭喜2024届高考的同学们&#xff0c;你们已经通过了高考的考验&#xff0c;即将进入人生的新阶段——大学。 现在正是高考完填报志愿的时刻&#xff0c;Left听到身边朋友提到报考志愿的诸多问题&#xff1a; 志愿填报怎么填&#xff1f;我要报考什么专业&#xff1f;这个…

[Cloud Networking] OSPF

OSPF 开放式最短路径优先&#xff08;Open Shortest Path First&#xff09;是一种动态路由协议&#xff0c;它属于链路状态路由协议&#xff0c;具有路由变化收敛速度快、无路由环路、支持变长子网掩码和汇总、层次区域划分等优点。 1 OSPF Area 为了适应大型网络&#xff0…

可编程定时计数器8253/8254 - 8253入门

时钟-给设备打拍子 概述 在计算机系统中&#xff0c;为了使所有设备之间的通信井然有序&#xff0c;各通信设备间必须有统一的节奏&#xff0c;不能各干各的&#xff0c;这个节奏就被称为定时或时钟 时钟并不是计算机处理速度的衡量&#xff0c;而是一种使设备间相互配合而避…

Linux 磁盘挂载与分区

Linux 磁盘挂载与分区 vda1: 其中vd表示虚拟磁盘&#xff0c;a表示第一块磁盘&#xff0c;b表示第二块磁盘&#xff0c;1表示第一块磁盘的第一分区&#xff08;显然两块磁盘都只有一个分区&#xff09;图中可以看到&#xff0c;vda1磁盘只有一个分区&#xff0c;且全部挂载到根…

vue3使用vant4的列表vant-list点击进入详情自动滚动到对应位置,踩坑日记(一天半的踩坑经历)

1.路由添加keepAlive <!-- Vue3缓存组件&#xff0c;写法和Vue2不一样--><router-view v-slot"{ Component }"><keep-alive><component :is"Component" v-if"$route.meta.keepAlive"/></keep-alive><component…

如何在MySQL中按字符串中的数字排序

在管理数据库时&#xff0c;我们经常遇到需要按嵌入在字符串中的数字进行排序的情况。这在实际应用中尤为常见&#xff0c;比如文件名、代码版本号等字段中通常包含数字&#xff0c;而这些数字往往是排序的关键。本文将详细介绍如何在MySQL中利用正则表达式提取字符串中的数字并…

Java家教系统小程序APP公众号h5源码

让学习更高效&#xff0c;更便捷 &#x1f31f; 引言&#xff1a;家教新选择&#xff0c;小程序来助力 在快节奏的现代生活中&#xff0c;家长们越来越注重孩子的教育问题。然而&#xff0c;如何为孩子找到一位合适的家教老师&#xff0c;成为了许多家长头疼的问题。现在&…

谷歌个人号,20人连续封测14天所需设备该怎么解决?

现在&#xff0c;在Google Play上架应用&#xff0c;对于大部分开发者来说&#xff0c;真的是不小的挑战&#xff0c;因为目前谷歌上架政策越来越严格了。特别是从2023年11月13日起&#xff0c;新政策要求个人开发者账号的应用必须经过20个独立用户连续14天的封闭测试&#xff…

【C语言】--分支和循环(1)

&#x1f37f;个人主页: 起名字真南 &#x1f9c7;个人专栏:【数据结构初阶】 【C语言】 目录 前言1 if 语句1.1 if1.2 else1.3 嵌套if1.4 悬空else 前言 C语言是结构化的程序设计语言&#xff0c;这里的结构指的是顺序结构、选择结构、循环结构。 我们可以用if、switch实现分支…

vue2实例实现一个初步的vuex

vue2实例实现一个初步的vuex 实现源码&#xff1a;vue2-review 1.App.vue 2.store目录下的index.js 3.效果 微信公众号&#xff1a;刺头拾年

扩展阅读:什么是中断

如果用一句话概括操作系统的原理,那就是:整个操作系统就是一个中断驱动的死循环,用最简单的代码解释如下: while(true){doNothing(); } 其他所有事情都是由操作系统提前注册的中断机制和其对应的中断处理函数完成的。我们点击一下鼠标,敲击一下键盘,执行一个程序,…

Spring MVC中的DispatcherServlet、HandlerMapping和ViewResolver的作用

在Spring MVC框架中&#xff0c;DispatcherServlet、HandlerMapping和ViewResolver是核心组件&#xff0c;它们各自承担着不同的角色和任务&#xff1a; 1.DispatcherServlet&#xff1a;它是Spring MVC生命周期中的前端控制器&#xff0c;负责接收HTTP请求并将它们分发给相应的…

5.x86游戏实战-CE定位基地址

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;4.x86游戏实战-人物状态标志位 上一个内容通过CE未知的初始值、未变动的数值、…

JVM相关总结

JVM的些许问题 1.JVM内存区域划分 2.JVM类加载过程 3.JVM的垃圾回收机制 1.JVM的内存区域划分 一个运行起来的Java进程就是一个JVM虚拟机,需要从操作系统申请一大片内存,就会把内存划分成几个区域,每个区域都有不同的作用 常见的面试题 2.JVM类加载过程 熟练背诵 ! ! !…

WordPress中文网址导航栏主题风格模版HaoWa

模板介绍 WordPress响应式网站中文网址导航栏主题风格模版HaoWa1.3.1源码 HaoWA主题风格除行为主体导航栏目录外&#xff0c;对主题风格需要的小控制模块都开展了敞开式的HTML在线编辑器方式的作用配备&#xff0c;另外预埋出默认设置的编码构造&#xff0c;便捷大伙儿在目前…

入门JavaWeb之 JDBC 连接数据库

JDBC&#xff1a;Java Database Connectivity&#xff0c;Java 数据库连接 需要 jar 包支持&#xff1a; java.sql javax.sql mysql-connector-java&#xff08;连接驱动&#xff0c;必须导入&#xff09; 在 MySQL 先建个 jdbc 数据库后 USE jdbc; 执行后再 CREATE TABLE…

15- 22题聚合函数 - 高频 SQL 50 题基础版

目录 1. 相关知识点2. 例子2.15 - 有趣的电影2.16 - 平均售价2.17 - 项目员工 I2.18 - 各赛事的用户注册率2.19 - 查询结果的质量和占比2.20 - 每月交易 I2.21 - 即时食物配送 II2.22 - 游戏玩法分析 IV 1. 相关知识点 函数 函数含义order by排序group by分组between 小值 an…