- 图形学
- 图像处理
- 跨平台
【免费下载链接】SkiaSharp
SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
本文基于 SkiaSharp 仓库中 SkiaSharp.Views.Forms.dll 的 1.68.2 API diff 文档 展开,面向在 Xamarin.Forms 中集成 SkiaSharp 渲染、尤其是使用 GTK 桌面后端的开发者。文章完整梳理
SkiaSharp.Views.Forms命名空间在 1.68.2 版中的全部公开类型与成员,并结合仓库内共享源码、GTK 视图实现与发布说明,解释SKCanvasView/SKGLView的绘制模型、触摸事件参数、ImageSource子类桥接、类型转换扩展及渲染器的工作原理。读完本文,你将能够准确理解 1.68.x 时代 Xamarin.Forms + SkiaSharp 视图层的 API 布局,并能依据 API 签名与源码路径在自己的应用中正确使用这些控件与事件参数。
背景:1.68.2 为 Xamarin.Forms 带来 GTK 视图后端
SkiaSharp 1.68.2 是 2020 年 4 月 30 日发布的稳定版本(见 1.68.2 发布说明),其 Platform 章节明确记载了本次引入的两项关键平台工作:
- 为 Xamarin.Forms 增加 GTK 视图后端(对应 PR #1089);
- 将捆绑的 Xamarin.Forms 基线更新到 4.2.0.910310,并始终引用 netstandard 以保证下游目标解析一致(对应 PR #1109、#1250)。
本文所讨论的 API diff 文档正是该 GTK 后端对应的程序集SkiaSharp.Views.Forms.dll(GTK 变体)的完整 API 表面。整个SkiaSharp.Views.Forms命名空间在 1.68.2 中均为新引入,程序集版本由 0.0.0.0 变更为 1.68.0.0。同一版本的其余平台 diff 可参见 1.68.2 API 变更索引,其中将SkiaSharp.Views.Forms本体标记为 ⚠️ breaking,而 GTK 变体为新增。
适用前提说明:
SkiaSharp.Views.Forms属于 Xamarin.Forms 时代的绑定层。从仓库 packages.md 中的包映射表 可见,后续版本中SkiaSharp.Views.Forms由SkiaSharp.Views.Maui.Controls取代、SkiaSharp.Views.Forms.GTK由SkiaSharp.Views.Gtk3取代。本文所有 API 签名均以 1.68.2 实际发布的程序集为准。
核心视图控件:SKCanvasView 与 SKGLView
该命名空间的灵魂是两套可直接放入 Xamarin.Forms 页面(Xamarin.Forms.View子类)的渲染控件,分别对应 CPU 光栅化与 OpenGL 硬件加速两条渲染路径。
SKCanvasView:CPU 光栅化视图
public class SKCanvasView : Xamarin.Forms.View, ISKCanvasViewController, System.ComponentModel.INotifyPropertyChanged, Xamarin.Forms.IAnimatable, Xamarin.Forms.IElementController, Xamarin.Forms.IGestureRecognizers, Xamarin.Forms.ITabStopElement, Xamarin.Forms.IViewController, Xamarin.Forms.IVisualElementController, Xamarin.Forms.Internals.IDynamicResourceHandler, Xamarin.Forms.Internals.IGestureController, Xamarin.Forms.Internals.INameScope, Xamarin.Forms.Internals.INavigationProxy { public SKCanvasView (); public static Xamarin.Forms.BindableProperty EnableTouchEventsProperty; public static Xamarin.Forms.BindableProperty IgnorePixelScalingProperty; public SkiaSharp.SKSize CanvasSize { get; } public bool EnableTouchEvents { get; set; } public bool IgnorePixelScaling { get; set; } public event System.EventHandler<SKPaintSurfaceEventArgs> PaintSurface; public event System.EventHandler<SKTouchEventArgs> Touch; public void InvalidateSurface (); protected override Xamarin.Forms.SizeRequest OnMeasure (double widthConstraint, double heightConstraint); protected virtual void OnPaintSurface (SKPaintSurfaceEventArgs e); protected virtual void OnTouch (SKTouchEventArgs e); }关键成员语义:
CanvasSize(只读):当前画布的像素尺寸。注意画布尺寸可能不同于视图尺寸——它受设备像素密度影响,与 GTK 原生视图 SKDrawingArea.CanvasSize 的文档说明一致:"The canvas size may be different to the view size as a result of the current device's pixel density."EnableTouchEvents:布尔值,决定是否启用触摸/鼠标事件分发。置为true后,Touch事件才会被触发。IgnorePixelScaling:布尔值,置为true时忽略设备像素密度,以逻辑像素为单位进行绘制。PaintSurface事件与OnPaintSurface虚方法:两种绘制入口。继承自SKCanvasView时若覆写OnPaintSurface,需调用base.OnPaintSurface(e)以继续触发事件——这一约定在 GTK 原生视图 SKDrawingArea 的文档注释 中被明确强调。InvalidateSurface():请求重绘。1.68.2 的发布说明提到本版本对视图生命周期与重绘做了整体重构(PR #1180、#1213),修复了排队重绘与重复加载问题。
SKGLView:OpenGL 硬件加速视图
public class SKGLView : Xamarin.Forms.View, ISKGLViewController, /* ...同 SKCanvasView 的接口列表... */ { public SKGLView (); public static Xamarin.Forms.BindableProperty EnableTouchEventsProperty; public static Xamarin.Forms.BindableProperty HasRenderLoopProperty; public SkiaSharp.SKSize CanvasSize { get; } public bool EnableTouchEvents { get; set; } public SkiaSharp.GRContext GRContext { get; } public bool HasRenderLoop { get; set; } public event System.EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface; public event System.EventHandler<SKTouchEventArgs> Touch; public void InvalidateSurface (); protected override Xamarin.Forms.SizeRequest OnMeasure (double widthConstraint, double heightConstraint); protected virtual void OnPaintSurface (SKPaintGLSurfaceEventArgs e); protected virtual void OnTouch (SKTouchEventArgs e); }与SKCanvasView的差异集中在两点:
GRContext(只读):当前 OpenGL 上下文对应的 SkiaGRContext,可用于在绘制前创建 GPU 资源(纹理、图像等);HasRenderLoop:连续渲染模式开关。置为true时视图进入持续重绘的渲染循环,适合动画/实时场景;false时仅按需重绘(InvalidateSurface或系统触发)。
视图控制器接口
两个视图分别通过ISKCanvasViewController与ISKGLViewController暴露内部契约,供渲染器与视图之间通信:
public interface ISKCanvasViewController : Xamarin.Forms.IElementController, Xamarin.Forms.IViewController, Xamarin.Forms.IVisualElementController { public event System.EventHandler<SkiaSharp.Views.Forms.GetPropertyValueEventArgs<SkiaSharp.SKSize>> GetCanvasSize; public event System.EventHandler SurfaceInvalidated; public virtual void OnPaintSurface (SKPaintSurfaceEventArgs e); public virtual void OnTouch (SKTouchEventArgs e); } public interface ISKGLViewController : Xamarin.Forms.IElementController, Xamarin.Forms.IViewController, Xamarin.Forms.IVisualElementController { public event System.EventHandler<SkiaSharp.Views.Forms.GetPropertyValueEventArgs<SkiaSharp.SKSize>> GetCanvasSize; public event System.EventHandler<SkiaSharp.Views.Forms.GetPropertyValueEventArgs<SkiaSharp.GRContext>> GetGRContext; public event System.EventHandler SurfaceInvalidated; public virtual void OnPaintSurface (SKPaintGLSurfaceEventArgs e); public virtual void OnTouch (SKTouchEventArgs e); }其中GetCanvasSize/GetGRContext属于请求-响应式事件:事件参数GetPropertyValueEventArgs<T>携带可写的Value属性,处理方写入值后返回,事件发起方即获得结果:
public class GetPropertyValueEventArgs<T> : System.EventArgs { public GetPropertyValueEventArgs (); public T Value { get; set; } }绘制事件参数:SKPaintSurfaceEventArgs 与 SKPaintGLSurfaceEventArgs
两个PaintSurface事件分别使用专用的参数类型,它们的源码实现在仓库共享层 source/SkiaSharp.Views/SkiaSharp.Views.Shared/ 中,被 iOS/Android/GTK 等各平台复用(通过条件编译切换命名空间)。
SKPaintSurfaceEventArgs(CPU 绘制)
public class SKPaintSurfaceEventArgs : System.EventArgs { public SKPaintSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.SKImageInfo info); // 属性 public SkiaSharp.SKImageInfo Info { get; } public SkiaSharp.SKSurface Surface { get; } }对应源码 SKPaintSurfaceEventArgs.cs 表明:单参数构造(surface, info)实际委托给三参数构造(surface, info, info),并额外暴露了 diff 文档中未列出的RawInfo属性(底层表面的真实尺寸与颜色类型)。典型用法即从e.Surface.Canvas取画布、以e.Info.Width/Height作为绘制边界。
SKPaintGLSurfaceEventArgs(GPU 绘制)
GL 版本的 API 表面更复杂,且带有一个已过时的构造重载:
public class SKPaintGLSurfaceEventArgs : System.EventArgs { public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTarget renderTarget); [Obsolete ("Use SKPaintGLSurfaceEventArgs(SKSurface, GRBackendRenderTarget, SKColorType, GRSurfaceOrigin) instead.")] public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTargetDesc renderTarget); public SKPaintGLSurfaceEventArgs (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTarget renderTarget, SkiaSharp.GRSurfaceOrigin origin, SkiaSharp.SKColorType colorType); public SkiaSharp.GRBackendRenderTarget BackendRenderTarget { get; } public SkiaSharp.SKColorType ColorType { get; } public SkiaSharp.GRSurfaceOrigin Origin { get; } [Obsolete ("Use BackendRenderTarget instead.")] public SkiaSharp.GRBackendRenderTargetDesc RenderTarget { get; } public SkiaSharp.SKSurface Surface { get; } }源码层面有三处值得注意的实现细节(见 SKPaintGLSurfaceEventArgs.cs):
- 默认参数:
(surface, renderTarget)构造默认使用GRSurfaceOrigin.BottomLeft与SKColorType.Rgba8888创建事件参数; - 废弃轨迹:旧版基于
GRBackendRenderTargetDesc的构造与RenderTarget属性均被标记[Obsolete],应迁移到GRBackendRenderTarget+ 显式origin/colorType的重载——这正是 1.68.2 API 演进中 "GL 渲染修正"(确保绘制前 GL 上下文为 current,PR #1141)的一部分; - Info 派生:完整构造会由
renderTarget.Width/Height与ColorType生成Info(即new SKImageInfo(renderTarget.Width, renderTarget.Height, ColorType))。
触摸事件体系:SKTouchEventArgs 与配套枚举
EnableTouchEvents = true后,Touch事件携带SKTouchEventArgs。该类型在 1.68.2 中加入了来自社区贡献的设备类型与压感支持(PR #1191、#1212,发布说明中记载为 "adding pressure and eraser handling")。
public class SKTouchEventArgs : System.EventArgs { public SKTouchEventArgs (long id, SKTouchAction type, SkiaSharp.SKPoint location, bool inContact); public SKTouchEventArgs (long id, SKTouchAction type, SKMouseButton mouseButton, SKTouchDeviceType deviceType, SkiaSharp.SKPoint location, bool inContact); public SKTouchEventArgs (long id, SKTouchAction type, SKMouseButton mouseButton, SKTouchDeviceType deviceType, SkiaSharp.SKPoint location, bool inContact, int wheelDelta); public SKTouchEventArgs (long id, SKTouchAction type, SKMouseButton mouseButton, SKTouchDeviceType deviceType, SkiaSharp.SKPoint location, bool inContact, int wheelDelta, float pressure); public SKTouchAction ActionType { get; } public SKTouchDeviceType DeviceType { get; } public bool Handled { get; set; } public long Id { get; } public bool InContact { get; } public SkiaSharp.SKPoint Location { get; } public SKMouseButton MouseButton { get; } public float Pressure { get; } public int WheelDelta { get; } public override string ToString (); }各成员说明:
| 成员 | 类型 | 含义 |
|---|---|---|
Id | long | 触摸点/指针的唯一标识,用于多点触摸时追踪同一触点 |
ActionType | SKTouchAction | 当前动作类型(按下/移动/释放/进入/退出等) |
Location | SKPoint | 触点位置(逻辑坐标) |
InContact | bool | 是否与表面保持接触 |
MouseButton | SKMouseButton | 触发的鼠标按键(仅鼠标设备有效) |
DeviceType | SKTouchDeviceType | 输入设备类型:触摸 / 鼠标 / 触控笔 |
Pressure | float | 压感值(支持压感的设备提供,如触控笔) |
WheelDelta | int | 滚轮增量(WPF 等桌面端滚轮事件,PR #1079) |
Handled | bool | 可写:置为true表示事件已被处理,阻止继续传递 |
三个配套枚举
[Serializable] public enum SKTouchAction { Entered = 0, Pressed = 1, Moved = 2, Released = 3, Cancelled = 4, Exited = 5, WheelChanged = 6, } [Serializable] public enum SKTouchDeviceType { Touch = 0, Mouse = 1, Pen = 2, } [Serializable] public enum SKMouseButton { Unknown = 0, Left = 1, Middle = 2, Right = 3, }SKTouchAction覆盖了从触点进入(Entered)到离开(Exited)的完整生命周期,WheelChanged专门用于桌面端滚轮输入;SKMouseButton的Unknown用于非鼠标设备。
ImageSource 桥接:将 Skia 对象直接作为 Xamarin.Forms 图像源
命名空间提供了四个Xamarin.Forms.ImageSource子类,使SKBitmap、SKImage、SKPicture、SKPixmap可以直接赋值给Image.Source等属性,并配合 GTK 平台的SKImageSourceHandler完成到Gdk.Pixbuf的加载。
四个 ImageSource 子类
public sealed class SKBitmapImageSource : Xamarin.Forms.ImageSource, System.ComponentModel.INotifyPropertyChanged, /* ...Xamarin.Forms 内部接口... */ { public SKBitmapImageSource (); public static Xamarin.Forms.BindableProperty BitmapProperty; public SkiaSharp.SKBitmap Bitmap { get; set; } public override System.Threading.Tasks.Task<bool> Cancel (); protected override void OnPropertyChanged (string propertyName); public static SKBitmapImageSource op_Implicit (SkiaSharp.SKBitmap bitmap); public static SkiaSharp.SKBitmap op_Implicit (SKBitmapImageSource source); } public sealed class SKImageImageSource : Xamarin.Forms.ImageSource { /* Image 属性 + 双向隐式转换 */ } public sealed class SKPixmapImageSource : Xamarin.Forms.ImageSource { /* Pixmap 属性 + 双向隐式转换 */ } public sealed class SKPictureImageSource : Xamarin.Forms.ImageSource { public static Xamarin.Forms.BindableProperty DimensionsProperty; public static Xamarin.Forms.BindableProperty PictureProperty; public SkiaSharp.SKSizeI Dimensions { get; set; } public SkiaSharp.SKPicture Picture { get; set; } public static SkiaSharp.SKPicture op_Explicit (SKPictureImageSource source); // 显式转换 }共性设计:每个子类都以对应的 Skia 对象为绑定属性(BitmapProperty/ImageProperty/PixmapProperty/PictureProperty),提供双向隐式转换(SKPictureImageSource为显式转换op_Explicit),因此可直接书写source = bitmap;或SKBitmap bmp = imageSource;。SKPictureImageSource额外需要Dimensions(SKSizeI)来指定矢量图栅格化尺寸。
GTK 平台加载器与 breaking 变化
public sealed class SKImageSourceHandler : Xamarin.Forms.IRegisterable, Xamarin.Forms.Platform.GTK.Renderers.IImageSourceHandler { public SKImageSourceHandler (); public virtual System.Threading.Tasks.Task<Gdk.Pixbuf> LoadImageAsync ( Xamarin.Forms.ImageSource imagesource, System.Threading.CancellationToken cancelationToken, float scale); }该处理器负责把上述任意ImageSource子类在 GTK 端异步加载为Gdk.Pixbuf。其底层像素拷贝逻辑可在 GTK 扩展类 GTKExtensions.cs 中找到印证:ToPixbuf(this SKImage)创建Colorspace.Rgb带 alpha 的 8 位Pixbuf,通过SKPixmap包装像素指针执行ReadPixels,并在Bgra8888颜色类型下调用SKSwizzle.SwapRedBlue交换 R/B 通道以匹配 GTK 的字节序。
本版本对该类型有一处破坏性变更(见 1.68.2 breaking diff):移除了面向 Tizen 的重载LoadImageAsync(Xamarin.Forms.Platform.Tizen.Native.Image image, Xamarin.Forms.ImageSource imageSource, System.Threading.CancellationToken cancelationToken),Tizen 调用方需迁移到上述统一签名。而到 1.68.2.1 的 GTK diff 为止,GTK 变体已无进一步 API 变化("No changes")。
类型转换扩展:Extensions 静态类
public static class Extensions { // Xamarin.Forms ← SkiaSharp public static Xamarin.Forms.Color ToFormsColor (this SkiaSharp.SKColor color); public static Xamarin.Forms.Point ToFormsPoint (this SkiaSharp.SKPoint point); public static Xamarin.Forms.Point ToFormsPoint (this SkiaSharp.SKPointI point); public static Xamarin.Forms.Rectangle ToFormsRect (this SkiaSharp.SKRect rect); public static Xamarin.Forms.Rectangle ToFormsRect (this SkiaSharp.SKRectI rect); public static Xamarin.Forms.Size ToFormsSize (this SkiaSharp.SKSize size); public static Xamarin.Forms.Size ToFormsSize (this SkiaSharp.SKSizeI size); // SkiaSharp ← Xamarin.Forms public static SkiaSharp.SKColor ToSKColor (this Xamarin.Forms.Color color); public static SkiaSharp.SKPoint ToSKPoint (this Xamarin.Forms.Point point); public static SkiaSharp.SKRect ToSKRect (this Xamarin.Forms.Rectangle rect); public static SkiaSharp.SKSize ToSKSize (this Xamarin.Forms.Size size); }Extensions提供 SkiaSharp 基础几何/颜色类型与 Xamarin.Forms 对应类型之间的双向转换,覆盖SKColor ↔ Color、SKPoint/SKPointI ↔ Point、SKRect/SKRectI ↔ Rectangle、SKSize/SKSizeI ↔ Size共 11 个扩展方法。这组方法常用于:把SKCanvasView.CanvasSize或触摸坐标转成 Xamarin.Forms 布局/手势系统的类型。其设计模式与 GTK 侧 GTKExtensions.cs 中SKPointI ↔ Gdk.Point、SKRectI ↔ Gdk.Rectangle、SKColor ↔ Gdk.Color(使用常量ColorMultiplier = 65535f / 255f处理 16 位色深)的扩展一一对应,可见 SkiaSharp 各平台绑定均遵循"扩展方法做类型互转"的同一套架构约定。
渲染器:SKCanvasViewRenderer 与渲染基类
GTK 后端的桥接由两个渲染器类型完成,它们把 Xamarin.Forms 视图映射到 GTK 原生控件:
public class SKCanvasViewRenderer : SKCanvasViewRendererBase<SKCanvasView, SkiaSharp.Views.Gtk.SKWidget>, Atk.Implementor, GLib.IWrapper, System.Collections.IEnumerable, System.IDisposable, Xamarin.Forms.IEffectControlProvider, Xamarin.Forms.IRegisterable, Xamarin.Forms.Platform.GTK.IVisualElementRenderer, Xamarin.Forms.Platform.GTK.IVisualNativeElementRenderer { public SKCanvasViewRenderer (); } public abstract class SKCanvasViewRendererBase<TFormsView, TNativeView> : Xamarin.Forms.Platform.GTK.ViewRenderer<TFormsView, TNativeView>, /* ...同上接口列表... */ { protected SKCanvasViewRendererBase (); protected virtual TNativeView CreateNativeControl (); protected override void Dispose (bool disposing); protected override void OnElementChanged (Xamarin.Forms.Platform.GTK.ElementChangedEventArgs<TFormsView> e); protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e); }- 泛型基类:
SKCanvasViewRendererBase<TFormsView, TNativeView>继承自 Xamarin.Forms 的 GTKViewRenderer<TFormsView, TNativeView>,在 1.68.2 中实例化为SKCanvasViewRenderer : SKCanvasViewRendererBase<SKCanvasView, SkiaSharp.Views.Gtk.SKWidget>,即用 GTK 的SKWidget承载SKCanvasView; - 生命周期钩子:
OnElementChanged在 Forms 元素挂接时创建原生控件(CreateNativeControl)并订阅事件;OnElementPropertyChanged负责把EnableTouchEvents、IgnorePixelScaling等绑定属性的变化同步到原生层;Dispose负责释放原生资源——1.68.2 专门修复了视图生命周期与释放问题(发布说明 PR #1180、#1135、#1133),强调在视图卸载时正确释放 Skia 对象。
需要说明的是,GTK 端真正的绘制宿主是仓库 SkiaSharp.Views.Gtk3 的 SKDrawingArea:它在OnDrawn中按分配尺寸创建SKImageInfo(SKImageInfo.PlatformColorType+Premulalpha),通过SKSurface.Create(imgInfo, pix.DataPtr, imgInfo.RowBytes)把 CairoImageSurface的像素指针直接包装为 SkiaSKSurface,绘制完成后Flush并以SKSwizzle.SwapRedBlue修正通道顺序再交给 Cairo 呈现。这从源码层面印证了SKCanvasView在 GTK 桌面端"Forms 视图 → SKWidget → SKDrawingArea/Cairo 像素共享"的完整链路。
版本对照与迁移提示
- 1.68.2(本文基线):
SkiaSharp.Views.Forms(含 GTK、WPF 变体)命名空间整体新建,程序集版本 1.68.0.0;GTK 后端首次随 1.68.2 发布说明 提供;SKImageSourceHandler移除 Tizen 专属重载(breaking)。 - 1.68.2.1:GTK 变体 无 API 变化。
- 后续演进:从仓库 releases 目录结构看,
SkiaSharp.Views.Forms及 GTK/WPF 变体在 2.80.x、2.88.x 系列仍持续发布;而在当前仓库的 包文档 packages.md 中,Xamarin.Forms 时代的SkiaSharp.Views.Forms系已被 .NET MAUI 的SkiaSharp.Views.Maui.Controls(提供SKCanvasView/SKGLView,net8.0+)与各平台原生包(如 GTK3 的SKDrawingArea)取代。若在全新项目中使用 SkiaSharp 的跨平台视图能力,建议直接采用 MAUI/Uno 控件;若维护 1.68.x 时代的 Xamarin.Forms 项目,本文的 API 签名即为可依赖的权威依据。
延伸阅读
- 本文主文档:1.68.2 SkiaSharp.Views.Forms(GTK)API diff
- 同版本整体变更:1.68.2 发布说明 与 API 变更索引
- 破坏性变更明细:1.68.2 breaking diff
- 事件参数共享源码:SKPaintSurfaceEventArgs.cs、SKPaintGLSurfaceEventArgs.cs
- GTK 原生实现:SKDrawingArea.cs、GTKExtensions.cs
- 包体系与迁移映射:packages.md
- 图形学
- 图像处理
- 跨平台
【免费下载链接】SkiaSharp
SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
相关推荐
SkiaSharp 1.68.2 版本变更深度解析:Xamarin.Forms 的 SKGLView 控制器接口与触摸压感(Pressure)支持
SkiaSharp 1.68.2 版本变更深度解析:Xamarin.Forms 的 SKGLView 控制器接口与触摸压感(Pressure)支持 本篇技术指南
图形学图像处理跨平台SkiaSharp.Views.Forms API 详解:在 Xamarin.Forms 中集成 SKCanvasView 与 SKGLView 绘制 2D 图形
SkiaSharp.Views.Forms API 详解:在 Xamarin.Forms 中集成 SKCanvasView 与 SKGLView 绘制 2D 图
图形学图像处理跨平台SkiaSharp 手指绘画实战:用 SKPath 与 SKCanvasView 触摸事件打造多点触控绘图应用
SkiaSharp 手指绘画实战:用 SKPath 与 SKCanvasView 触摸事件打造多点触控绘图应用 本文以 .NET MAUI 应用为场景,讲解如何
图形学图像处理跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考