ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

miniblink49 中的 Skia 绘图 API 总览:以 SkCanvas、SkPaint 为核心理解绘图上下文、状态划分与效果对象体系

miniblink49 中的 Skia 绘图 API 总览:以 SkCanvas、SkPaint 为核心理解绘图上下文、状态划分与效果对象体系 miniblink49 中的 Skia 绘图 API 总览以 SkCanvas、SkPaint 为核心理解绘图上下文、状态划分与效果对象体系【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49本文为 miniblink49 仓库内置 Skia 引擎的用户文档 API 总览页 的深度解读Skia 的绘图 API 围绕SkCanvas组织每次 draw 调用都由“图元对象SkRect、SkPath等 样式对象SkPaint”两部分构成。读完本篇你将掌握 Skia 中 Canvas 与 Paint 的状态划分原则、四个核心类的职责边界、各类后端Raster / Ganesh / PDF / Picture下创建SkCanvas的标准写法以及 Shader、Xfermode、ImageFilter 等效果对象的完整分类并能定位到 miniblink 合成器实际使用这些 API 的源码位置。一、核心心智模型SkCanvas 是唯一的“绘制上下文”原文档开篇即给出 Skia API 的组织原则Skia is organized around theSkCanvasobject。SkCanvas是所有 draw 调用的宿主典型的如drawRect、drawPath、drawText等。每一次绘制调用都包含两个组成部分被绘制的图元primitiveSkRect、SkPath、SkBitmap等描述画什么形状颜色与样式属性SkPaint描述怎么画。最简示例继承自原文档canvas-drawRect(rect, paint);在 SkCanvas 头文件 中可以确认这套接口是虚函数体系客户端只需面向SkCanvas*编程而无需关心底层后端// third_party/skia/include/core/SkCanvas.h class SK_API SkCanvas : public SkRefCnt { ... virtual void drawRect(const SkRect rect, const SkPaint paint); // L684 virtual void drawPath(const SkPath path, const SkPaint paint); // L770 void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint paint, const SkEncoding* encoding nullptr); // L906 };二、状态划分Canvas 只存变换与裁剪Paint 存一切样式这是原文档最核心的设计阐述也是 Skia 区别于 PostScript、Cairo、AWT 等同类上下文 API 的关键点SkPaint承载了描述如何绘制的大部分状态颜色、填充还是描边、如何与已绘制内容混合xfermode、字体、抗锯齿、描边宽度等。Paint 不维护内部状态栈没有 save/restore 概念但它很轻量调用方可以自由创建、维护任意数量的 paint 对象各自配置好特定用途。SkCanvas只保存相对少的状态它指向实际被绘制的像素目标并维护一个矩阵栈和裁剪栈。因此在canvas-drawRect(rect, paint)这一调用中Canvas 的当前矩阵可以变换矩形的坐标平移、旋转、斜切、透视Canvas 的当前 clip可以限制矩形绘制到画布的哪个区域而其余所有样式属性全部由 paint 控制。将颜色、线宽等样式属性从 canvas 状态中剥离到 paint 对象使得 canvas 的save()/restore()只需要维护矩阵与 clip 两个栈效率更高这一点在同目录 SkPaint 文档 中有明确说明。一个典型的状态操作示例保存状态 → 变换 → 绘制 → 恢复状态样式完全由 paint 描述void draw(SkCanvas* canvas) { canvas-save(); canvas-translate(SkIntToScalar(128), SkIntToScalar(128)); canvas-rotate(SkIntToScalar(45)); SkRect rect SkRect::MakeXYWH(-90.5f, -90.5f, 181.0f, 181.0f); SkPaint paint; paint.setColor(SK_ColorBLUE); canvas-drawRect(rect, paint); canvas-restore(); }上述代码源自 SkCanvas 文档会绘制一个旋转 45 度的矩形矩形长什么样、什么颜色由 paint 决定画在哪里、旋转多少由 canvas 的矩阵栈决定。关于清空画布文档给出了两级简化路径// 方式一用 paint 填充整个画布仍受当前 clip 约束 // 若 paint 中带 shader还会尊重 canvas 当前矩阵 SkPaint paint; paint.setColor(SK_ColorWHITE); canvas-drawPaint(paint); // 方式二只想画纯色可带 xfermode时直接 drawColor()免去分配 paint canvas-drawColor(SK_ColorWHITE);其余所有 draw API 都遵循同样的模式以 paint 参数收尾。需要注意文档强调的细节部分调用传的是 paint 指针const SkPaint*此时 paint 可以为 null其他按值传递的 paint 参数则是必填的。完整的一组图元调用示范矩形、圆角矩形、椭圆、圆、路径、位图、文本见 skcanvas.md 中的drawRect / drawRRect / drawCircle / drawPath / drawBitmap / drawBitmapRect / drawText综合示例可直接作为 API 调用清单参考。三、四个核心类绘制上下文、样式、矩形、区域原文档将使用 SkCanvas API归纳为四个入口类本仓库中它们各自对应一份头文件与一份用户文档类职责原文档表述头文件本仓库配套用户文档SkCanvas绘制上下文drawing contextinclude/core/SkCanvas.hskcanvas.mdSkPaint颜色、描边、字体、效果include/core/SkPaint.hskpaint.mdSkRect矩形include/core/SkRect.hskrect.mdSkRegion矩形与路径的集合运算include/core/SkRegion.hskregion.md此外矩阵类SkMatrix有独立文档 skmatrix.md用于理解 canvas 矩阵栈背后的变换表示平移、旋转、斜切、透视。四、附录如何创建 SkCanvas 对象多后端原文档的附录指向 Creating SkCanvas Objects。Skia 提供多种接收SkCanvas绘制命令的后端每个后端都有唯一的创建方式这是实际使用中最容易踩坑的部分。逐一说明如下4.1 Raster纯 CPU绘制到内存块内存可由 Skia 管理也可由客户端管理。推荐方式是通过SkSurface统一管理#include SkData.h #include SkImage.h #include SkStream.h #include SkSurface.h void raster(int width, int height, void(*draw)(SkCanvas*), const char* path) { SkAutoTUnrefSkSurface rasterSurface( SkSurface::NewRasterN32Premul(width, height)); SkCanvas* rasterCanvas rasterSurface-getCanvas(); draw(rasterCanvas); SkAutoTUnrefSkImage img(rasterSurface-newImageSnapshot()); if (!img) { return; } SkAutoTUnrefSkData png(img-encode()); if (!png) { return; } SkFILEWStream out(path); (void)out.write(png-data(), png-size()); }也可以显式提供像素内存让客户端自行管理 surface 的存储std::vectorchar raster_direct(int width, int height, void(*draw)(SkCanvas*)) { SkImageInfo info SkImageInfo::MakeN32(width, height); size_t rowBytes info.minRowBytes(); size_t size info.getSafeSize(rowBytes); std::vectorchar pixelMemory(size); // 客户端分配内存 SkAutoTUnrefSkSurface surface( SkSurface::NewRasterDirect(info, pixelMemory[0], rowBytes)); SkCanvas* canvas surface-getCanvas(); draw(canvas); return std::move(pixelMemory); }4.2 GaneshGPU 加速后端Ganesh surface 必须有一个管理 GPU 上下文及纹理、字体缓存的GrContext对象#include GrContextFactory.h #include SkSurface.h void ganesh(int width, int height, void(*draw)(SkCanvas*), const char* path) { GrContextFactory grFactory; GrContext* context grFactory.get(GrContextFactory::kNative_GLContextType); SkImageInfo info SkImageInfo::MakeN32Premul(width, height); SkAutoTUnrefSkSurface gpuSurface( SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info)); if (!gpuSurface) { SkDebugf(SkSurface::NewRenderTarget returned null\n); return; } SkCanvas* gpuCanvas gpuSurface-getCanvas(); draw(gpuCanvas); /* 之后同样通过 newImageSnapshot()/encode() 导出 PNG */ }4.3 SkPDFPDF 文档生成PDF 文档天然包含多个页面因此使用SkDocument而不是SkSurface#include SkDocument.h #include SkStream.h void skpdf(int width, int height, void(*draw)(SkCanvas*), const char* path) { SkFILEWStream pdfStream(path); SkAutoTUnrefSkDocument pdfDoc(SkDocument::CreatePDF(pdfStream)); SkCanvas* pdfCanvas pdfDoc-beginPage(SkIntToScalar(width), SkIntToScalar(height)); draw(pdfCanvas); pdfDoc-close(); }4.4 SkPicture显示列表/Display List 格式使用SkPictureRecorder替代SkSurface把绘制命令录制成可序列化、可重放的 picture#include SkPictureRecorder #include SkPicture #include SkStream.h void picture(int width, int height, void(*draw)(SkCanvas*), const char* path) { SkPictureRecorder recorder; SkCanvas* recordingCanvas recorder.beginRecording(SkIntToScalar(width), SkIntToScalar(height)); draw(recordingCanvas); SkAutoTUnrefSkPicture picture(recorder.endRecordingAsPicture()); SkFILEWStream skpStream(path); picture-serialize(skpStream); // 生成 .skp 文件 }Display List 录制—回放正是 miniblink 自身合成管线的核心模式从源码文件分布看cc/raster/下的 RasterTask.cpp、RasterTaskWorkerThread.cpp 与DisplayListRecordingSourcecc/playback/DisplayListRecordingSource.h构成了录制显示列表 工作线程回放的结构且 cc/trees/LayerTreeHost.cpp、cc/layers/CompositingLayer.cpp、cc/tiles/Tile.cpp 等合成器文件均直接引用了SkCanvas/SkPaint源码中可检索到。4.5 其余后端NullCanvas忽略所有绘制命令的空实现仅供测试用SkAutoTDeleteSkCanvas c(SkCreateNullCanvas());见 SkNullCanvas.h。SkXPS仍为实验性写入 XPS 文档方式与 SkPDF 类似SkDocument::CreateXPS(stream)后beginPage()取 canvas。SkSVG仍为实验性写入 SVG 文档通过SkSVGCanvas::Create(rect, xmlWriter)创建 canvas。4.6 跨后端一致性示例原文档附录给出了一个用同一draw()函数驱动四种后端的单元测试范式void example(SkCanvas* canvas) { const SkScalar scale 256.0f; const SkScalar R 0.45f * scale; const SkScalar TAU 6.2831853f; SkPath path; for (int i 0; i 5; i) { SkScalar theta 2 * i * TAU / 5; if (i 0) { path.moveTo(R * cos(theta), R * sin(theta)); } else { path.lineTo(R * cos(theta), R * sin(theta)); } } path.close(); SkPaint p; p.setAntiAlias(true); canvas-clear(SK_ColorWHITE); canvas-translate(0.5f * scale, 0.5f * scale); canvas-drawPath(path, p); } DEF_TEST(FourBackends, r) { raster( 256, 256, example, out_raster.png ); ganesh( 256, 256, example, out_ganesh.png ); skpdf( 256, 256, example, out_skpdf.pdf ); picture(256, 256, example, out_picture.skp); }这个范式的价值在于验证后端无关性业务绘制代码只面向SkCanvas切换后端不需要改一行绘制逻辑。五、重要类速查表原文档 Doxygen 清单的本地落地原文档后半部分是一份Auto-generated Doxygen Documentation清单列出了 Skia 中较重要的类。原始链接指向外部 Doxygen 站点本仓库内不再提供但这些类在本地 Skia 源码中都有对应的头文件可直接以仓库内文件作为权威 API 参考。下表将原文档清单与本地头文件一一对应类原文档清单本地头文件作用简述SkCanvasinclude/core/SkCanvas.h绘制上下文矩阵/clip 栈宿主SkImageinclude/core/SkImage.h不可变位图/纹理抽象SkSurfaceinclude/core/SkSurface.h管理绘制目标内存取 canvas 的入口SkPaintinclude/core/SkPaint.h颜色、样式、字体与效果载体SkXfermodeinclude/core/SkXfermode.h源/目标像素混合Porter-Duff 等├SkLerpXfermode/SkPixelXorXfermode同上线性插值混合 / 像素异或SkShaderinclude/core/SkShader.h渐变、噪声、位图纹理等颜色生成器├SkComposeShader/SkPerlinNoiseShader/SkGradientShader/SkTransparentShader同上着色器组合 / Perlin 噪声 / 渐变 / 透明SkColorFilterinclude/core/SkColorFilter.hxfermode 之前修改源颜色├SkColorCubeFilter/SkColorMatrixFilter/SkLumaColorFilter/SkModeColorFilter同上颜色立方体 / 颜色矩阵 / 亮度 / 色调模式SkPathEffectinclude/core/SkPathEffect.h生成 alpha mask 前的路径几何修改├SkPath2DPathEffect/SkLine2DPathEffect/SkPath1DPathEffect/SkArcToPathEffect/SkCornerPathEffect/SkDashPathEffect/SkDiscretePathEffect/SkComposePathEffect/SkSumPathEffect同上2D/1D 变形、圆角、虚线、离散、组合等路径效果SkImageFilterinclude/core/SkImageFilter.h对已绘制图像做后处理├ 18 个子类SkAlphaThresholdFilter、SkBlurImageFilter、SkBitmapSource、SkColorFilterImageFilter、SkComposeImageFilter、SkDisplacementMapEffect、SkDownSampleImageFilter、SkDropShadowImageFilter、SkLightingImageFilter、SkMagnifierImageFilter、SkMatrixConvolutionImageFilter、SkMergeImageFilter、SkDilateImageFilter、SkErodeImageFilter、SkOffsetImageFilter、SkPictureImageFilter、SkRectShaderImageFilter、SkTileImageFilter、SkXfermodeImageFilter同上模糊、位移贴图、降采样、阴影、光照、膨胀腐蚀、偏移、平铺等SkMaskFilterinclude/core/SkMaskFilter.h上色前修改 alpha mask├SkEmbossMaskFilter/SkTableMaskFilter同上浮雕 / 查找表含模糊效果实现路径SkDrawLooperinclude/core/SkDrawLooper.h一次绘制多次执行如阴影、轮廓└SkBlurDrawLooper同上模糊轮廓循环绘制说明上表子类名均继承自原文档的 Doxygen 类清单是当时版本 Skia 公开 API 的索引具体成员函数签名请以对应本地头文件为准本仓库 Skia 为较老版本部分较新 API 如SkImageFilter::Blur静态工厂可能不存在。六、Paint 与六类效果对象效果管线视角SkPaint 文档 进一步说明除颜色、描边、文本等简单属性外paint 支持六类可挂接的效果对象它们都是绘制管线的不同环节的子类替换点且均为引用计数对象赋给 paint 时 paint 会加引用、换掉或析构时自动unref()效果类型管线位置典型用途SkPathEffect生成 alpha mask 之前虚线、几何变形SkRasterizer自定义 mask 层合成阴影SkMaskFiltermask 上色之前模糊、浮雕SkShader颜色生成线性/径向/扫描渐变、位图平铺clamp/repeat/mirrorSkColorFilterxfermode 之前颜色矩阵等SkXfermode像素混合Porter-Duff、blend modes挂接 shader 的引用计数用法原文档给出的标准模式SkShader* shader SkGradientShader::CreateLinear(/* ... */); paint.setShader(shader); // paint 内部 addRef shader-unref(); // 抵消创建时的引用此后 paint 是唯属主paint 还持有SkTypeface引用用于文本测量与绘制可直接调用measureText / getTextBounds / textToGlyphs / getFontMetrics等 API 而不必经过 canvas。七、适用前提与注意事项版本前提本仓库third_party/skia/是一份随 miniblink 一起发布的 Skia 快照含 Doxyfile、skia.gyp构建配置上述 API 描述均对应该快照内的头文件。若将代码移植到新版 Skia需重新核对接口新版 Skia 已引入SkPaint非 const 传参、SkImageInfo等重构。实验性后端SkXPS 与 SkSVG 在原文档中明确标注 still experimental仅建议用于验证目的。文档定位用户文档位于third_party/skia/site/user/API 章节入口为 index.md面向把 HTML UI 集成进应用的 miniblink 场景阅读它主要是为了理解内核绘制与合成管线中 Skia 接口的语义实际业务代码一般不直接操作这些类而是经由cc/目录下的合成器LayerTreeHost、RasterTask、Tile 等间接使用。链接说明原文档中指向外部 Doxygen 站点的链接已在本仓库失效请一律改读本仓库内third_party/skia/include/core/下的头文件作为 API 权威依据。小结Skia 的 API 设计可以压缩为一句话形状归图元位置归 Canvas风格归 Paint特效归效果对象。SkCanvas只维护矩阵栈与 clip 栈并指向像素目标SkPaint承载全部样式并可挂接六类引用计数的效果对象SkRect/SkRegion提供几何与区域运算基础不同后端Raster、Ganesh、SkPDF、SkPicture 等只改变 canvas 的创建方式绘制代码本身保持完全一致。在 miniblink49 中这套 API 支撑着 cc/ 合成器的显示列表录制与栅格化回放是理解其 HTML UI 渲染链路不可绕开的一层。【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表