【SkiaSharp绘图11】SKCanvas属性详解

news2025/2/23 18:57:22

文章目录

  • SKCanvas
  • 构造SKCanvas
    • 构造光栅 Surface
    • 构造GPU Surface
    • 构造PDF文档
    • 构造XPS文档
    • 构造SVG文档
    • SKNoDrawCanvas
  • 变换
  • 剪裁和状态
  • 构造函数
  • 相关属性
    • DeviceClipBounds获取裁切边界(设备坐标系)
    • ClipRect修改裁切区域
    • IsClipEmpty当前裁切区域是否为空
    • IsClipRect裁切区域是否为矩形
    • LocalClipBounds获取裁切边界(本地坐标系)
    • SaveCount 状态记数
    • TotalMatrix 变换矩阵
    • 示例

SKCanvas

用于在位图、表面或其他绘图设备上执行 2D 图形操作。

  1. 绘图操作

    • SKCanvas 提供了丰富的绘图方法,如绘制几何图形(圆形、矩形、路径等)、文本、位图等。
    • 可以设置画笔(SKPaint)的颜色、样式、线宽等属性,实现各种绘制效果。
  2. 图像合成和组合

    • 可以将多个绘图操作合成为单个图像,支持透明度和混合模式,实现复杂的图形效果。
    • 支持在不同图层上进行绘制,并可以进行图层的合并和组合。
  3. 处理和转换

    • 支持坐标变换(平移、旋转、缩放等),方便实现复杂的图形变换和动画效果。
    • 可以通过剪切区域(ClipRectClipPath)限制绘制区域,优化绘制性能。
  4. 跨平台支持

    • SkiaSharp 是跨平台的,可以在 Windows、macOS、Linux 和移动平台(Android、iOS)上使用,SKCanvas 提供了统一的绘图 API。
  5. 高性能渲染

    • SkiaSharp 使用 GPU 加速和多线程优化,能够高效地处理大规模的图形渲染和复杂的图形操作。

构造SKCanvas

构造光栅 Surface

光栅后端绘制到内存块。该内存可由SkiaSharp或客户端自行管理。
默认推荐使用管理绘制画布命令的内存的对象SKSurface。
OnPaintSurface(object sender, SkiaSharp.Views.Desktop.SKPaintGLSurfaceEventArgs e)事件中e参数的Surface就是SKSurface对象。
可通过SKSurface.Create方法的不同参数创建由SkiaSharp或自行管理内存的SKSurface对象。

  1. 由SkiaSharp管理
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);

var info = new SKImageInfo(400, 300);
//通过SKSurface获取Canvas
using (var skSurface = SKSurface.Create(info))
using (var skCanvas = skSurface.Canvas)
using (var skPaint = new SKPaint())
{
    skPaint.TextSize = 18;
    skPaint.Color = SKColors.LightGreen;
    skPaint.IsStroke = true;
    skCanvas.DrawRect(100, 100, 250, 100, skPaint);
    skCanvas.DrawText($"SKSurface1", 0, 200, skPaint);
    canvas.DrawSurface(skSurface, 0, 0, skPaint);
}
  1. 自动分配内存
var info2 = new SKImageInfo(400, 400);
//自行分配内存
var memory = Marshal.AllocCoTaskMem(info2.BytesSize);

try
{
    using (var surface2 = SKSurface.Create(info2, memory, info2.RowBytes))
    using (var canvas2 = surface2.Canvas)
    using (var paint = new SKPaint())
    {
        paint.Color = SKColors.Blue;
        paint.TextSize = 18;
        canvas2.DrawCircle(200, 200, 100, paint);
        canvas2.DrawText($"SKSurface2", 0, 200, paint);
        canvas.DrawSurface(surface2, 0, 350, paint);
    }
}
finally
{
    Marshal.FreeCoTaskMem(memory);
}

SKSurface.Create
在测试的过程,一开始不知为什么会出现两个矩形,还以为是SkiaSharp的BUG,后不知怎么搞的,又没事了…

构造GPU Surface

GPU Surface必须有一个GRContext对象来管理GPU上下文以及纹理和字体的相关缓存。
GRContext对象与OpenGL上下文或Vulkan设备一一匹配。
如果使用SKGLControl控件,默认的的e.Surface.Context就是启用了GPU的OpenGL

构造PDF文档

使用SkiaSharp直接绘制并生成PDF文档(不支持显示PDF)
PDF后端使用SKDocument而不是SKSurface,因为文档必须包含多个页面。

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

using (FileStream stream = new FileStream(@"Images\test.pdf", FileMode.CreateNew, FileAccess.Write))
using (var doc = SKDocument.CreatePdf(stream, 72))
using (var pdfCanvas = doc.BeginPage(600, 800))
using (var paint = new SKPaint())
{
    paint.Color = SKColors.LightGreen;
    paint.IsAntialias = true;
    paint.TextSize = 24;
    pdfCanvas.DrawText("Create PDF Doc by SkiaSharp", 20, 30, paint);
    pdfCanvas.DrawCircle(300, 400, 150, paint);
    doc.EndPage();
    doc.Close();
}
  1. 创建一个写的文件流
  2. 根据文件流和DPI创建SKDocument对象
  3. 根据SKDocument对象创建SKCanvas
  4. 可以SKCanvas绘制内容,生成PDF。

SKDocument.CreatePdf

构造XPS文档

与构造Pdf类似,用SKDocument.CreateXps

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

using (FileStream stream = new FileStream(@"Images\test.xps", FileMode.Create, FileAccess.Write))
using (var doc = SKDocument.CreatePdf(stream, 72))
using (var pdfCanvas = doc.BeginPage(600, 800))
using (var paint = new SKPaint())
{
    paint.Color = SKColors.LightGreen;
    paint.IsAntialias = true;
    paint.TextSize = 24;
    pdfCanvas.DrawText("Create XPS doc by SkiaSharp", 20, 30, paint);
    pdfCanvas.DrawCircle(300, 400, 150, paint);
    doc.EndPage();
    doc.Close();
}

在这里插入图片描述

构造SVG文档

使用SKSvgCanvas构造SVG文档。

using (FileStream stream = new FileStream(@"Images\test.svg", FileMode.Create, FileAccess.Write))
using (var svgCanvas = SKSvgCanvas.Create(new SKRect(0, 0, 600, 800), stream))
using (var paint = new SKPaint())
{
    paint.Color = SKColors.LightGreen;
    paint.IsAntialias = true;
    paint.TextSize = 24;
    svgCanvas.DrawText("Create SVG doc by SkiaSharp", 20, 30, paint);
    svgCanvas.DrawCircle(300, 400, 150, paint);
}

SKSvgCanvas.Create

SKNoDrawCanvas

构造不绘制的画布,是提供一个“无操作”的画布,这意味着它不会进行任何实际的绘图操作。一般用于性能测试、绘图命令的记录与分析、避免不必要的绘图。

变换

SKCanvas提供Scale、Skew、Translate、RotateDegrees、RotateRadians等常用的二维变换。
还可以使用SetMatrix指定变换矩阵。
使用ResetMatrix可重置矩阵状态。

剪裁和状态

可使用变换矩阵的Save方法来保存当前变换状态,然后使用Restore或RestoreToCount方法恢复以前的状态。还有SaveLayer方法。

构造函数

public SKCanvas (SkiaSharp.SKBitmap bitmap);

创建一个画布,其中包含要绘制的指定位图。

相关属性

DeviceClipBounds获取裁切边界(设备坐标系)

public SkiaSharp.SKRectI DeviceClipBounds { get; }

获取当前裁切边界(在设备坐标中)。

ClipRect修改裁切区域

public void ClipRect (SkiaSharp.SKRect rect, SkiaSharp.SKClipOperation operation = SkiaSharp.SKClipOperation.Intersect, bool antialias = false);

修改当前裁切区域

IsClipEmpty当前裁切区域是否为空

public bool IsClipEmpty { get; }

判断当前裁切区域是否为空。

IsClipRect裁切区域是否为矩形

public bool IsClipRect { get; }

判断当前裁切区域是否为矩形。

LocalClipBounds获取裁切边界(本地坐标系)

public SkiaSharp.SKRect LocalClipBounds { get; }

获取当前的裁切边界(本地坐标系中,矩阵变换后的)

SaveCount 状态记数

public int SaveCount { get; }

获取画布私有堆栈上的矩阵/裁切状态的数量。
当调用SKCanvas对象的Save()方法时,加1。调用Restore()时,减1。
一个新的画布的SaveCount为1。

TotalMatrix 变换矩阵

public SkiaSharp.SKMatrix TotalMatrix { get; }

获取当前画布的变换矩阵。

示例

var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);
using (var paint = new SKPaint())
{
    paint.Color = SKColors.Red;
    paint.IsAntialias = true;
    paint.TextSize = 24;
    var yOffset = 50F;
    if (canvas.GetDeviceClipBounds(out var rect))
    {
        canvas.DrawText($"DeviceClipBounds:{rect}", 20, yOffset, paint);
    }
    yOffset += paint.FontSpacing;
    rect = new SKRectI(25, 20, 800, 800);
    //设置裁切区域
    canvas.ClipRect(rect);

    canvas.DrawText($"ClipRect:{rect}", 20, yOffset, paint);
    yOffset += paint.FontSpacing;
    if (canvas.GetDeviceClipBounds(out rect))
    {
        canvas.DrawText($"DeviceClipBounds:{rect}", 20, yOffset, paint);
        yOffset += paint.FontSpacing;
    }

    canvas.DrawText($"IsClipEmpty:{canvas.IsClipEmpty}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;
    canvas.DrawText($"IsClipRect:{canvas.IsClipRect}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;

    rect = SKRectI.Create(100, 50);
    canvas.DrawText($"ClipRect:{rect} Difference", 50, yOffset, paint);
    yOffset += paint.FontSpacing;
    canvas.ClipRect(rect, SKClipOperation.Difference);

    canvas.DrawText($"IsClipRect:{canvas.IsClipRect}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;

    canvas.DrawText($"LocalClipBounds:{canvas.LocalClipBounds}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;

    // 设置画布变换(如平移)
    canvas.Translate(50, 50);
    canvas.DrawText($"Translate(50, 50)", 50, yOffset, paint);
    yOffset += paint.FontSpacing;
    canvas.DrawText($"LocalClipBounds:{canvas.LocalClipBounds}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;
    if (canvas.GetDeviceClipBounds(out rect))
    {
        canvas.DrawText($"DeviceClipBounds:{rect}", 50, yOffset, paint);
        yOffset += paint.FontSpacing;
    }

    canvas.DrawText($"SaveCount:{canvas.SaveCount}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;
    canvas.Save();

    canvas.DrawText($"Call Save()", 50, yOffset, paint);
    yOffset += paint.FontSpacing;

    canvas.DrawText($"SaveCount:{canvas.SaveCount}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;

    canvas.Restore();

    canvas.DrawText($"Call Restore()", 50, yOffset, paint);
    yOffset += paint.FontSpacing;

    canvas.DrawText($"SaveCount:{canvas.SaveCount}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;

    using (var newCanvas = new SKCanvas(new SKBitmap()))
    {
        canvas.DrawText($"new SKCanvas SaveCount:{newCanvas.SaveCount}", 50, yOffset, paint);
        yOffset += paint.FontSpacing;
    }

    var matrix = canvas.TotalMatrix;
    canvas.DrawText($"TotalMatrix:{string.Join(",",matrix.Values)}", 50, yOffset, paint);
    yOffset += paint.FontSpacing;
}

SKCanvas属性

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

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

相关文章

2023: 芒种集•序言

2023: 芒种集•序言 2023: 芒种集•序言 从西南旅游回来,一直忙着整理游记“2024:追寻红色足迹”,之后又应初建平索要刘桂蓉遗作“我们一起走过”,于是把“别了,老屋”和诗作“二月”一并合编,把我写的悼念…

线性代数基础概念:矩阵

目录 线性代数基础概念:矩阵 1. 矩阵的定义 2. 矩阵的运算 3. 矩阵的特殊类型 4. 矩阵的秩 5. 矩阵的初等变换 6. 矩阵的特征值与特征向量 7. 矩阵的应用 8. 矩阵总结 总结 线性代数基础概念:矩阵 矩阵是线性代数中的另一个重要概念&#xff0…

数据结构——

1. 什么是并查集? 在计算机科学中,并查集(英文:Disjoint-set data structure,直译为不数据结构交集)是一种数据结构,用于处理一些不交集(Disjoint sets,一系列没有重复元…

使用 position:absolute; 定位的元素在ios上被遮盖

在ios上一个元素使用position:absolute;定位后,被其它元素遮盖 在使用absolute的元素上加一行代码 -webkit-transform: translate3d(0, 0, 0);

【YashanDB知识库】YAS-00103 no free block in dictionary cache

【问题分类】功能使用 【关键字】YAS-00103,no free block in dictionary cache 【问题描述】执行union all 太多子查询导致报错,例子如下: 【问题原因分析】 选择增大DICTIONARY_CACHE_SIZE 或 SHARE_POOL_SIZE 或 两者都增大 【解决/规…

杭州代理记账报税全程托管专业实力全面指南

杭州代理记税报税服务可以为企业提供全程托管财务管理解决方案,确保企业的财务工作专业、高效、合规。以下是杭州代理记税报税服务全面指南: https://www.9733.cn/news/detail/185.html 一、代理记账报税服务的内容 基础服务: 每日记&#xf…

使用supportFragmentManager管理多个fragment切换

android studio创建的项目就没有一个简单点的框架,生成的代码都是繁琐而复杂,并且不实用。 国内的页面一般都是TAB页面的比较多,老外更喜欢侧边菜单。 如果我们使用一个activity来创建程序,来用占位符管理多个fragment切换&…

Android性能分析工具-Perfetto基本使用

文章目录 一、Perfetto介绍二、抓取方法2.1 手机端直接抓取2.1.1 打开系统跟踪2.1.2 开始录制 2.2 使用 adb 抓取2.3 通过 Perfetto 网页自定义抓取 三、trace分析方法3.1 打开trace文件3.2 查看方法 一、Perfetto介绍 Perfetto 是一个用于性能检测和跟踪分析的生产级开源堆栈。…

CSS 文本输入框右下角的尺寸控件(三斜线:-webkit-resizer)消除,以及如何配置其样式,添加 resize 让标签元素可进行拖拽放大。

前言:在日常的前端开发中,不管是原始的和 还在在各类组件库中的文本输入框中,元素内容的右下角总是有一个三斜线的样式,本文简单了解它是什么?如何去控制并修改样式? 一、它是? 这三个斜线其实…

python---OpenCv(二),背景分离方法较有意思

目录 边界矩形 旋转矩形(最小外接矩形): 计算轮廓 找4个点的坐标 把浮点型转为Int 画轮廓 边界矩形--(最大外接矩形) 转灰度 找轮廓 找顶点 画矩形 显示 背景分离方法(这个很好玩,可以识别在动的物体) 边…

如何提高项目风险的处理效率?5个重点

提高项目风险的处理效率,有助于迅速识别和应对风险,减少风险导致的延误,降低成本,提升项目质量,确保项目按时交付。如果项目风险处理效率较低,未能及时发现和处理风险,导致问题累积,…

文化财经wh6boll带macd多空转折点提示指标公式源码

文化财经wh6boll带macd多空转折点提示指标公式源码: DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); MACD:2*(DIFF-DEA); MID:MA(CLOSE,26);//求N个周期的收盘价均线,称为布林通道中轨 TMP2:STD(CLOSE,26);//求M个周期内的收盘价的标准差 …

【计算机网络体系结构】计算机网络体系结构实验-www服务器配置管理实验

一、实验内容 www服务器配置管理, wireshark数据包分析 二、实验目的 1. 了解WWW服务的体系结构与工作原理,掌握利用Microsoft的IIS实现WWW服务的基本配置,掌握WEB站点的管理 2. 利用Wireshark抓取http数据包进行分析。运行软件Wireshark…

ARM 240625

练习: 汇编实现1-100累加,结果保存在r0 .text 声明下面内容都属于文本段内容 .globl _start 声明 _start 是一个全局启用的标签_start: 封装 _start 标签,汇编的标签和C中函数类似mov r0,#0 mov 把0 搬运到 r0 寄存器mov r1,#1 mov 把1 …

postman汉化中文(Windows)

Postman 是一款专业的 API 开发工具,为开发者提供了创建、测试、调试和分享 HTTP 请求的便利性和灵活性。其主要功能包括请求构建与发送、自动化测试、团队协作与分享、实时监视与调试以及环境与变量管理。无论是个人开发者还是团队,Postman 都能有效地提…

Fragment与ViewModel(MVVM架构)

简介 在Android应用开发中,Fragment和ViewModel是两个非常重要的概念,它们分别属于架构组件库的一部分,旨在帮助开发者构建更加模块化、健壮且易维护的应用。 Fragment Fragment是Android系统提供的一种可重用的UI组件,它能够作为…

【第14章】探索新技术:如何自学SD3模型(找官方资料/精读/下载/安装/3款工作流/效果测试)ComfyUI基础入门教程

近期,也就是2024年6月12日,StabilityAI开源了最新的SD3模型的2B版本,而神奇的是,ComfyUI早在6月11号就已经适配了SD3!相比之下,SD WebUI 的更新速度却远远落后... 所以,如果想要尝试一些AI绘画领域的新技术,ComfyUI是一个非常值得投入时间学习的工具。 这节课,我们就…

Ascend基于自定义算子工程的算子开发

环境准备 见https://gitee.com/zaj1414904389/ascend-tutorial.git 工程创建 CANN软件包中提供了工程创建工具msopgen,开发者可以输入算子原型定义文件生成Ascend C算子开发工程 [{"op": "AddCustom","input_desc": [{"name…

2024全网最全面及最新且最为详细的网络安全技巧四 之 sql注入以及mysql绕过技巧 (2)———— 作者:LJS

目录 4.5 DNS记录类型介绍(A记录、MX记录、NS记录等,TXT,CNAME,PTR) 4.5.1 DNS 4.5.2 A记录 4.5.3NS记录 4.5.4 MX记录 4.5.5 CNAME记录 4.5.6 TXT记录 4.5.7 泛域名与泛解析 4.5.8域名绑定 4.5.9 域名转向 4.6 Mysql报错注入之floor报错详解…

基于Java影院管理系统设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码数据库🌟感兴趣的可以先收藏起来,还…