- 图形学
- 图像处理
- 跨平台
【免费下载链接】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.Android 程序集 1.59.0 → 1.60.0 的 API 差异文档(SkiaSharp.Views.Android.md)展开。1.60.0 为该程序集引入了三个全新类型——GLTextureView、SKGLTextureView与SKGLTextureViewRenderer,为 .NET 绑定 Android 应用提供了基于TextureView的 OpenGL ES 硬件加速渲染通道。读完本文,你将掌握这套新视图类型的完整公开 API、内部 GL 线程与 EGL 生命周期机制,以及如何在自己的 Android 布局中接入并绘制 SkiaSharp GPU 内容。
一、变更概览:程序集版本与三个新类型
API 差异文档开篇即给出本次变更的核心信息:
Assembly Version Changed: 1.60.0.0 vs 1.59.0.0
即在 1.60.0 版本中,SkiaSharp.Views.Android.dll的程序集版本由 1.59.0.0 提升至 1.60.0.0,命名空间SkiaSharp.Views.Android下新增了三个公开类型:
| 新类型 | 职责 |
|---|---|
GLTextureView | 继承自Android.Views.TextureView,在专用线程上驱动 OpenGL ES 渲染的通用视图 |
SKGLTextureView | 继承自GLTextureView,为硬件加速的SKSurface提供专用渲染表面的 SkiaSharp 封装 |
SKGLTextureViewRenderer | 抽象渲染器,实现GLTextureView.IRenderer,把 GL 帧缓冲包装成可绘制的SKSurface |
三者形成清晰的层次:GLTextureView负责 EGL/GL 环境与渲染线程,SKGLTextureView负责把 SkiaSharp 绘制接到该环境上,SKGLTextureViewRenderer则负责每一帧的SKSurface创建与事件派发。对应实现位于 source/SkiaSharp.Views/SkiaSharp.Views/Platform/Android/ 下的GLTextureView.cs、SKGLTextureView.cs与SKGLTextureViewRenderer.cs。
二、GLTextureView:基于 TextureView 的 OpenGL ES 渲染视图
2.1 类型声明与构造
差异文档给出的完整声明如下:
public class GLTextureView : Android.Views.TextureView, Android.Graphics.Drawables.Drawable.ICallback, Android.Runtime.IJavaObject, Android.Views.Accessibility.IAccessibilityEventSource, Android.Views.KeyEvent.ICallback, Android.Views.TextureView.ISurfaceTextureListener, Android.Views.View.IOnLayoutChangeListener, Java.Interop.IJavaPeerable, System.IDisposable { // constructors public GLTextureView (Android.Content.Context context); public GLTextureView (Android.Content.Context context, Android.Util.IAttributeSet attrs); ... }两个构造函数分别对应「代码中程序化创建视图」与「从 Android XML 布局文件 inflate」两种场景。从源码看,构造函数都会调用私有Initialize()方法,完成两件关键初始化(GLTextureView.cs):
- 把视图自身注册为
SurfaceTextureListener,以接收SurfaceTexture的可用、销毁、尺寸变化回调; - 通过
AddOnLayoutChangeListener监听布局变化,并在OnLayoutChange回调中把新尺寸转发给渲染表面(GLTextureView.cs)。
2.2 核心属性与渲染控制
// properties public Android.Opengl.DebugFlags DebugFlags { get; set; } public bool PreserveEGLContextOnPause { get; set; } public Android.Opengl.Rendermode RenderMode { get; set; }DebugFlags:GL 调试开关。源码中当设置了CheckGlError或LogGlCalls时,会通过GLDebugHelper.Wrap对 GL 接口进行包装以检查错误或记录调用(GLTextureView.cs),主要用于排查渲染问题。PreserveEGLContextOnPause:是否在视图暂停时保留 EGL 上下文。GL 线程在暂停时默认会释放 EGL 表面,并视此属性决定是否连 EGL 上下文一并释放(GLTextureView.cs)。RenderMode:渲染模式,对应 Android 的Rendermode枚举。GL 线程只有在requestRender被置位或模式为Continuously时才真正绘制(GLTextureView.cs)。需要连续动画时使用Rendermode.Continuously,按需绘制时使用Rendermode.WhenDirty并配合RequestRender()。
2.3 生命周期方法:OnPause / OnResume
public void OnPause (); public void OnResume ();视图宿主必须在 Activity 暂停/恢复时调用这两个方法:OnPause()会暂停渲染线程,OnResume()会重建 OpenGL 显示并恢复渲染线程(GLTextureView.cs)。注意:两者都必须在设置 renderer 之后才能调用。
2.4 线程与任务投递
public void QueueEvent (Java.Lang.IRunnable r); public void QueueEvent (System.Action r); public void RequestRender ();GLTextureView内部维护一个名为GLThread的专用线程(GLTextureView.cs),所有 EGL 初始化、OnSurfaceCreated/OnSurfaceChanged/OnDrawFrame回调、eglSwapBuffers交换缓冲都在该线程内完成(GLTextureView.cs)。QueueEvent可以把任务(System.Action会被包装为Java.Lang.Runnable)投递到该线程队列中串行执行;RequestRender()则请求立刻渲染一帧。
2.5 EGL 配置方法族
public void SetEGLConfigChooser (GLTextureView.IEGLConfigChooser configChooser); public void SetEGLConfigChooser (bool needDepth); public void SetEGLConfigChooser (int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize); public void SetEGLContextClientVersion (int version); public void SetEGLContextFactory (GLTextureView.IEGLContextFactory factory); public void SetEGLWindowSurfaceFactory (GLTextureView.IEGLWindowSurfaceFactory factory); public void SetGLWrapper (GLTextureView.IGLWrapper glWrapper);这些方法对应 AndroidGLSurfaceView的经典可定制点:
SetEGLConfigChooser三个重载分别接受自定义选择器、仅按「是否需要深度缓冲」选择、或精确指定 RGBA/深度/模板各分量位数。默认行为是选择 RGB 888 且深度至少 16 位的配置(源码中默认SimpleEGLConfigChooser使用(8, 8, 8, 0, 深度16, 模板0),见 GLTextureView.cs)。当 EGL 客户端版本设为 2 时,配置规格还会自动追加EGL_RENDERABLE_TYPE/EGL_OPENGL_ES2_BIT,确保选到 ES 2.0 可渲染的配置(GLTextureView.cs)。SetEGLContextClientVersion(int version):指定 OpenGL ES 上下文版本,传 2 即 ES 2.0。DefaultContextFactory会据此构造EGL_CONTEXT_CLIENT_VERSION属性列表(GLTextureView.cs)。SetEGLContextFactory/SetEGLWindowSurfaceFactory/SetGLWrapper:分别定制 EGL 上下文的创建/销毁、窗口表面的创建/销毁、以及 GL 接口的包装(后者典型用途是调试追踪)。
以上配置方法均须在SetRenderer之前调用,源码中CheckRenderThreadState()会在已启动 GL 线程后再次调用时抛出异常(GLTextureView.cs)。
2.6 内嵌接口
差异文档完整列出了五个内嵌接口,它们定义了渲染与 EGL 的可扩展契约:
public interface IEGLConfigChooser { public virtual Javax.Microedition.Khronos.Egl.EGLConfig ChooseConfig (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display); } public interface IEGLContextFactory { public virtual Javax.Microedition.Khronos.Egl.EGLContext CreateContext (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLConfig eglConfig); public virtual void DestroyContext (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLContext context); } public interface IEGLWindowSurfaceFactory { public virtual Javax.Microedition.Khronos.Egl.EGLSurface CreateWindowSurface (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLConfig config, Java.Lang.Object nativeWindow); public virtual void DestroySurface (Javax.Microedition.Khronos.Egl.IEGL10 egl, Javax.Microedition.Khronos.Egl.EGLDisplay display, Javax.Microedition.Khronos.Egl.EGLSurface surface); } public interface IGLWrapper { public virtual Javax.Microedition.Khronos.Opengles.IGL Wrap (Javax.Microedition.Khronos.Opengles.IGL gl); } public interface IRenderer { public virtual void OnDrawFrame (Javax.Microedition.Khronos.Opengles.IGL10 gl); public virtual void OnSurfaceChanged (Javax.Microedition.Khronos.Opengles.IGL10 gl, int width, int height); public virtual void OnSurfaceCreated (Javax.Microedition.Khronos.Opengles.IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config); public virtual void OnSurfaceDestroyed (); }其中IRenderer是最核心的契约:OnSurfaceCreated在 EGL 上下文创建后调用(可用于创建配套 pbuffer 等),OnSurfaceChanged在表面创建及尺寸变化时调用(典型做法是设置视口),OnDrawFrame每帧调用,OnSurfaceDestroyed在表面丢失时调用。GL 线程在eglSwapBuffers返回EGL_CONTEXT_LOST或其他错误时,也会走对应的上下文/表面重建路径(GLTextureView.cs)。
三、SKGLTextureView:面向 SKSurface 的硬件加速视图
3.1 类型声明与自动配置
public class SKGLTextureView : SkiaSharp.Views.Android.GLTextureView, Android.Graphics.Drawables.Drawable.ICallback, ... { // constructors public SKGLTextureView (Android.Content.Context context); public SKGLTextureView (Android.Content.Context context, Android.Util.IAttributeSet attrs); // properties public SkiaSharp.SKSize CanvasSize { get; } public SkiaSharp.GRContext GRContext { get; } // methods public virtual void SetRenderer (SKGLTextureView.ISKRenderer renderer); ... }SKGLTextureView的源码注释将其定位为「使用专用表面显示硬件加速SKSurface的GLTextureView实现」。它继承GLTextureView,构造时自动完成两件 EGL 配置(SKGLTextureView.cs):
private void Initialize() { SetEGLContextClientVersion(2); SetEGLConfigChooser(8, 8, 8, 8, 0, 8); renderer = new InternalRenderer(this); SetRenderer(renderer); }即OpenGL ES 2.0 上下文 + RGBA 8888 颜色、无深度缓冲、8 位模板缓冲的 EGL 配置,并内置了一个InternalRenderer(继承SKGLTextureViewRenderer),它会回调到外层视图的OnPaintSurface方法(SKGLTextureView.cs)。因此使用者无需手动SetRenderer。
3.2 两个只读属性
CanvasSize:当前画布尺寸(SKSize)。源码注释特别提醒:画布尺寸可能因设备像素密度而与视图尺寸不同(SKGLTextureView.cs)。GRContext:当前 GPU 上下文。SkiaSharp 的 GPU 绘制对象(纹理、着色器、渲染目标)都在该上下文上创建。
两者均直接转发自内部 renderer(SKGLTextureView.cs)。
3.3 绘制入口:PaintSurface 事件与 OnPaintSurface 方法
public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface; protected virtual void OnPaintSurface(SKPaintGLSurfaceEventArgs e);源码注释明确给出了两种绘制方式:
- 订阅
PaintSurface事件——最常用的方式,在事件处理器中通过e.Surface拿到SKSurface,用e.BackendRenderTarget.Width/Height获得表面尺寸,从surface.Canvas开始绘制,最后调用canvas.Flush():
myView.PaintSurface += (sender, e) => { var surface = e.Surface; var surfaceWidth = e.BackendRenderTarget.Width; var surfaceHeight = e.BackendRenderTarget.Height; var canvas = surface.Canvas; // draw on the canvas canvas.Flush(); };- 重写
OnPaintSurface——若重写此方法,必须调用base.OnPaintSurface(e),否则事件不会被触发(SKGLTextureView.cs):
protected override void OnPaintSurface(SKPaintGLSurfaceEventArgs e) { base.OnPaintSurface(e); var canvas = e.Surface.Canvas; // draw on the canvas canvas.Flush(); }3.4 事件数据:SKPaintGLSurfaceEventArgs
PaintSurface事件携带的SKPaintGLSurfaceEventArgs定义在 source/SkiaSharp.Views/SkiaSharp.Views.Shared/SKPaintGLSurfaceEventArgs.cs,主要成员如下:
| 成员 | 说明 |
|---|---|
SKSurface Surface | 当前正在绘制的表面 |
GRBackendRenderTarget BackendRenderTarget | 当前绘制所基于的 GL 后端渲染目标(含宽高) |
SKColorType ColorType | 渲染目标的颜色类型 |
GRSurfaceOrigin Origin | 渲染目标的表面原点 |
SKImageInfo Info | 表面的图像信息(尺寸 + 颜色类型) |
SKImageInfo RawInfo | 底层渲染目标的原始图像信息 |
它提供四个构造函数重载(SKPaintGLSurfaceEventArgs.cs):最简单的(SKSurface, GRBackendRenderTarget)重载默认采用GRSurfaceOrigin.BottomLeft与SKColorType.Rgba8888。
四、SKGLTextureViewRenderer:把 GL 帧缓冲变成 SKSurface
public abstract class SKGLTextureViewRenderer : Java.Lang.Object, ..., GLTextureView.IRenderer, System.IDisposable { // constructors protected SKGLTextureViewRenderer (); // properties public SkiaSharp.SKSize CanvasSize { get; } public SkiaSharp.GRContext GRContext { get; } // methods protected override void Dispose (bool disposing); public virtual void OnDrawFrame (Javax.Microedition.Khronos.Opengles.IGL10 gl); protected virtual void OnDrawFrame (SkiaSharp.SKSurface surface, SkiaSharp.GRBackendRenderTargetDesc renderTarget); public virtual void OnSurfaceChanged (Javax.Microedition.Khronos.Opengles.IGL10 gl, int width, int height); public virtual void OnSurfaceCreated (Javax.Microedition.Khronos.Opengles.IGL10 gl, Javax.Microedition.Khronos.Egl.EGLConfig config); public virtual void OnSurfaceDestroyed (); }这是连接 GL 世界与 Skia 世界的桥:它在 GL 线程的OnDrawFrame回调里,把当前 GL 帧缓冲包装为SKSurface,再调用受保护的OnDrawFrame(SKSurface, GRBackendRenderTargetDesc)让子类绘制。源码实现位于 SKGLTextureViewRenderer.cs。
4.1 默认的渲染目标格式
源码定义了两个常量作为默认格式(SKGLTextureViewRenderer.cs):
private const SKColorType colorType = SKColorType.Rgba8888; private const GRSurfaceOrigin surfaceOrigin = GRSurfaceOrigin.BottomLeft;即RGBA 8888 颜色类型 + 左下原点,与 OpenGL 的传统坐标取向一致。
4.2 OnDrawFrame 的完整流程
每一帧OnDrawFrame(IGL10 gl)的执行序列(SKGLTextureViewRenderer.cs):
- 清屏:
GLES10.GlClear清除颜色、深度、模板缓冲; - 惰性创建 GRContext:若尚未创建,通过
GRGlInterface.Create()拿到 GL 接口,再GRContext.CreateGl(glInterface)创建 Skia GPU 上下文; - 按需重建后端渲染目标:当渲染目标为空、尺寸变化或目标失效时,用
GLES20.GlGetIntegerv读取当前GL_FRAMEBUFFER_BINDING、模板位数与采样数(采样数会通过context.GetMaxSurfaceSampleCount(colorType)限制到硬件上限),构造GRGlFramebufferInfo与GRBackendRenderTarget,同时销毁旧的 surface 与 renderTarget; - 创建 SKSurface:
SKSurface.Create(context, renderTarget, surfaceOrigin, colorType)并取出canvas; - 派发绘制:在
SKAutoCanvasRestore保护下构造SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType),调用受保护的OnPaintSurface(e); - 冲刷:
canvas.Flush()与context.Flush(),把 Skia 命令提交给 GL。
4.3 生命周期与资源释放
OnSurfaceCreated与OnSurfaceDestroyed都会调用FreeContext(),释放 surface、renderTarget 与 GRContext(SKGLTextureViewRenderer.cs);Dispose(bool)在disposing时同样释放上述资源(SKGLTextureViewRenderer.cs);OnSurfaceChanged设置GLES20.GlViewport并记录新尺寸(SKGLTextureViewRenderer.cs)。
五、实战:在 Android 布局中接入 SKGLTextureView
仓库自带的 samples/Basic/Android 示例工程展示了最标准的接入方式:先在 XML 布局中声明视图,再在 Fragment 中订阅事件。
5.1 布局文件
在 fragment_gpu_texture.xml 中直接以全限定名放置视图:
<SkiaSharp.Views.Android.SKGLTextureView android:id="@+id/skiaView" android:layout_width="match_parent" android:layout_height="match_parent" />由于它本身就是一个 Android 视图,可以像普通视图一样参与布局、叠加与变换。
5.2 Fragment 中订阅绘制事件
GpuTextureFragment.cs 的核心逻辑:
skiaView = view.FindViewById<SKGLTextureView>(Resource.Id.skiaView); skiaView.PaintSurface += OnPaintSurface; private void OnPaintSurface(object sender, SKPaintGLSurfaceEventArgs e) { var canvas = e.Surface.Canvas; var width = e.BackendRenderTarget.Width; var height = e.BackendRenderTarget.Height; // 通过 SKRuntimeEffect 编译的 SkSL 着色器逐帧绘制 var builder = shaderBuilder.Value; builder.Uniforms["iTime"] = fpsCounter.ElapsedSeconds; builder.Uniforms["iResolution"] = new float[] { (float)width, (float)height }; using var shader = builder.Build(); using var paint = new SKPaint { Shader = shader }; canvas.DrawRect(0, 0, width, height, paint); }注意两点:尺寸取自e.BackendRenderTarget而非视图本身(确保与 GPU 表面一致);绘制完成后 Skia 会在渲染器内部统一Flush,事件处理器内只需完成绘制命令的录入。示例还展示了Touch事件与绘制线程的配合:触摸坐标归一化后作为 shader uniform 传入,实现「metaball lava lamp」的实时交互动画。Fragment 销毁时记得解绑事件并释放相关资源(GpuTextureFragment.cs)。
5.3 与 SKGLSurfaceView 的取舍
示例工程 README(samples/Basic/Android/README.md)对两种 GPU 视图的定位做了明确区分:
SKGLSurfaceView:基于 AndroidSurfaceView,适合全屏 GPU 内容;SKGLTextureView:基于TextureView,支持透明度、变换以及在视图层级内的合成——例如把 GPU 渲染结果与上层 UI 叠加、做缩放/旋转动画时,应优先选择后者。代价是TextureView相对SurfaceView存在一定的合成开销。
从实现上看,SKGLTextureView走的是「SurfaceTexture→ EGL window surface → GL 帧缓冲 →GRBackendRenderTarget→SKSurface」的完整链路,而GLTextureView基类本身不依赖任何 Skia 类型,因此也适用于在 SkiaSharp 之外直接承载自定义 OpenGL 渲染。
六、小结与进一步阅读
1.60.0 中SkiaSharp.Views.Android新增的GLTextureView/SKGLTextureView/SKGLTextureViewRenderer,补齐了该平台「基于 TextureView 的 GPU 渲染」能力:GLTextureView提供与 AndroidGLSurfaceView同构的 EGL 配置、渲染线程与生命周期管理;SKGLTextureView开箱即用地提供PaintSurface事件与CanvasSize/GRContext属性;SKGLTextureViewRenderer则在每帧完成 GL 帧缓冲到SKSurface的桥接与资源重建。
- API 差异文档原文:SkiaSharp.Views.Android.md
- 核心实现:GLTextureView.cs、SKGLTextureView.cs、SKGLTextureViewRenderer.cs
- 事件数据:SKPaintGLSurfaceEventArgs.cs
- 可运行示例:GpuTextureFragment.cs 与 fragment_gpu_texture.xml
如需在本地复现,可进入samples/Basic/Android目录,使用.NET 8 SDK并安装 Android 工作负载后执行dotnet build -t:Install -f net8.0-android部署到设备或模拟器,并在MainActivity.cs中将DefaultPage设为GpuTexture即可查看该视图的实际渲染效果。
- 图形学
- 图像处理
- 跨平台
【免费下载链接】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.59.2 的 SkiaSharp.Views.Android API 变更解析:SKGLSurfaceView 新增 GRContext 属性
SkiaSharp 1.59.2 的 SkiaSharp.Views.Android API 变更解析:SKGLSurfaceView 新增 GRContext
图形学图像处理跨平台解读 SkiaSharp GTK 视图的完整 API 面:从 1.60.0 引入的 SKWidget 到现代 SKDrawingArea
解读 SkiaSharp GTK 视图的完整 API 面:从 1.60.0 引入的 SKWidget 到现代 SKDrawingArea 本篇基于 SkiaSh
图形学图像处理跨平台SkiaSharp 1.60.0 发布说明解读:SKGLControl 新增两个 OpenTK GraphicsMode 构造函数
SkiaSharp 1.60.0 发布说明解读:SKGLControl 新增两个 OpenTK GraphicsMode 构造函数 本文围绕 SkiaSharp
图形学图像处理跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考