04 - Skia 技术全景:跨平台2D图形渲染引擎完整解析
Skia 是一个开源的完整 2D 图形库,为 Google Chrome、Android、Flutter 等主要平台提供渲染基础设施。名字来源于创始人的挪威语"滑雪"(ski)词根,象征着"平滑、流畅"的图形渲染体验。位置:语言: C++ (主要), C (部分底层代码)代码行数: ~850,000+ 行(包含测试)许可证: BSD 3-Clause核心理念:模块职责模块目录职责公共APIincl
04 - Skia 技术全景:跨平台2D图形渲染引擎完整解析
Chrome 与 Android 的图形基石 | 软硬件双渲染管线 | 85万行高性能C++代码
目录
概述
什么是 Skia?
Skia 是一个开源的完整 2D 图形库,为 Google Chrome、Android、Flutter 等主要平台提供渲染基础设施。名字来源于创始人的挪威语"滑雪"(ski)词根,象征着"平滑、流畅"的图形渲染体验。
位置: external/skia/
语言: C++ (主要), C (部分底层代码)
代码行数: ~850,000+ 行(包含测试)
许可证: BSD 3-Clause
核心特性
| 特性 | 说明 |
|---|---|
| 双渲染管线 | CPU软件渲染 + GPU硬件加速 |
| 跨平台 | Android、iOS、Linux、macOS、Windows、Web |
| 完整API | Path、Canvas、Paint、Shader、Filter等 |
| 高性能 | SIMD优化、GPU加速、智能缓存 |
| 丰富编解码器 | JPEG、PNG、WebP、HEIF、AVIF、JXL等11+格式 |
| 先进文本渲染 | HarfBuzz整形、距离场渲染、多语言支持 |
| 现代GPU架构 | Ganesh (当前) + Graphite (下一代) |
使用统计
- Android设备: 30+ 亿设备使用Skia渲染UI
- Chrome浏览器: 30+ 亿用户的网页渲染
- Flutter框架: 跨平台UI框架的底层引擎
- 其他: Firefox、Chromium OS、Fuchsia OS
架构哲学
"Skia is designed to be:
• Fast - Optimized for modern GPUs and CPUs
• Small - Minimal binary size for mobile
• Correct - Accurate rendering across platforms
• Complete - Full-featured graphics solution"
—— Skia 设计目标
核心理念:
- 性能优先: GPU加速 + CPU优化
- 平台一致性: 统一API,跨平台一致渲染
- 可扩展性: 模块化设计,易于扩展
- 向后兼容: API稳定,长期支持
架构设计
整体架构层次
┌─────────────────────────────────────────────┐
│ 应用层 (Application Layer) │
│ android.graphics.Canvas, View.onDraw() │
└────────────────────┬────────────────────────┘
↓ JNI
┌─────────────────────────────────────────────┐
│ 公共API层 (Public API - include/) │
│ SkCanvas, SkPaint, SkPath, SkImage, etc. │
└────────────────────┬────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 核心实现层 (Core Implementation) │
│ Canvas State, Path Ops, Matrix Transform │
└──────────┬──────────────────┬───────────────┘
↓ ↓
┌──────────────────┐ ┌─────────────────────┐
│ 软件渲染 (CPU) │ │ GPU渲染 (Ganesh) │
│ SkBitmapDevice │ │ GrRecordingContext │
│ SkBlitter │ │ GrOps, GrGpu │
└──────────┬──────┘ └────────┬────────────┘
↓ ↓
┌──────────────────┐ ┌─────────────────────┐
│ 内存帧缓冲 │ │ GPU命令缓冲区 │
│ 像素操作 │ │ OpenGL/Vulkan/Metal│
└──────────────────┘ └─────────────────────┘
模块职责
| 模块 | 目录 | 职责 |
|---|---|---|
| 公共API | include/core/, include/effects/ | Canvas绘图接口、图像处理 |
| 核心引擎 | src/core/ | 路径操作、矩阵变换、状态管理 |
| GPU后端 | src/gpu/ganesh/ | GPU渲染、资源管理、命令生成 |
| 软件后端 | src/core/SkBlitter*.cpp | CPU光栅化、像素混合 |
| 文本渲染 | src/text/, modules/skshaper/ | 字体整形、文本布局 |
| 图像编解码 | src/codec/ | JPEG/PNG/WebP等格式支持 |
| 特效系统 | src/effects/, src/shaders/ | 着色器、滤镜、路径特效 |
| 平台适配 | src/ports/ | OS相关实现(字体、文件I/O) |
数据流向
绘图指令 (drawRect, drawPath, ...)
↓
SkCanvas 记录状态(矩阵、裁剪)
↓
SkPaint 应用(颜色、着色器、特效)
↓
路径简化 & 裁剪
↓
渲染目标选择
├─→ CPU路径: SkBitmapDevice → SkBlitter → 像素内存
└─→ GPU路径: GrRecordingContext → GrOps → GPU驱动
核心组件
1. Canvas 绘图系统
1.1 SkCanvas 接口
位置: include/core/SkCanvas.h, src/core/SkCanvas.cpp
代码量: 3,155 行实现
核心绘图方法:
class SkCanvas {
public:
// 形状绘制
void drawRect(const SkRect& rect, const SkPaint& paint);
void drawOval(const SkRect& oval, const SkPaint& paint);
void drawRRect(const SkRRect& rrect, const SkPaint& paint);
void drawPath(const SkPath& path, const SkPaint& paint);
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint&);
void drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
bool useCenter, const SkPaint& paint);
// 图像绘制
void drawImage(const SkImage* image, SkScalar left, SkScalar top,
const SkSamplingOptions&, const SkPaint* paint = nullptr);
void drawImageRect(const SkImage*, const SkRect& src, const SkRect& dst,
const SkSamplingOptions&, const SkPaint*, SrcRectConstraint);
void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
const SkSamplingOptions&, const SkPaint* paint = nullptr);
// 文本绘制
void drawString(const char text[], SkScalar x, SkScalar y,
const SkFont& font, const SkPaint& paint);
void drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint);
void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[],
const uint32_t clusters[], int textByteCount, const char utf8text[],
SkPoint origin, const SkFont& font, const SkPaint& paint);
// 高级绘制
void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
const SkPaint& paint);
void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&);
void drawMesh(const SkMesh& mesh, sk_sp<SkBlender>, const SkPaint&);
void drawPicture(const SkPicture* picture);
void drawDrawable(SkDrawable* drawable);
// 变换操作
void translate(SkScalar dx, SkScalar dy);
void scale(SkScalar sx, SkScalar sy);
void rotate(SkScalar degrees);
void skew(SkScalar sx, SkScalar sy);
void concat(const SkMatrix& matrix);
void setMatrix(const SkM44& matrix); // 4x4矩阵(3D支持)
// 裁剪操作
void clipRect(const SkRect&, SkClipOp, bool doAntiAlias);
void clipRRect(const SkRRect&, SkClipOp, bool doAntiAlias);
void clipPath(const SkPath&, SkClipOp, bool doAntiAlias);
void clipShader(sk_sp<SkShader>, SkClipOp = SkClipOp::kIntersect);
// 状态管理
int save(); // 保存当前状态
int saveLayer(const SkRect* bounds, const SkPaint* paint); // 创建图层
int saveLayerAlphaf(const SkRect* bounds, float alpha);
void restore(); // 恢复状态
int getSaveCount() const;
void restoreToCount(int saveCount);
};
状态栈机制:
// 示例:状态保存与恢复
canvas->save(); // 保存点1
canvas->translate(100, 100);
canvas->drawRect(...);
canvas->save(); // 保存点2
canvas->rotate(45);
canvas->drawCircle(...);
canvas->restore(); // 恢复到保存点1
canvas->drawOval(...); // 仍然有translate(100,100)
canvas->restore(); // 恢复到初始状态
图层合成:
// 创建半透明图层
SkPaint layerPaint;
layerPaint.setAlpha(128); // 50%透明度
canvas->saveLayer(nullptr, &layerPaint);
// 在图层上绘制
canvas->drawCircle(100, 100, 50, paint1);
canvas->drawRect(SkRect::MakeXYWH(80, 80, 40, 40), paint2);
canvas->restore(); // 合成图层到主画布
1.2 SkPaint 绘制属性
位置: include/core/SkPaint.h
作用: 控制绘制样式(颜色、描边、特效等)
class SkPaint {
public:
// 颜色设置
void setColor(SkColor color);
void setColor4f(const SkColor4f& color, SkColorSpace* colorSpace);
void setAlphaf(float alpha);
// 绘制样式
enum Style {
kFill_Style, // 填充
kStroke_Style, // 描边
kStrokeAndFill_Style // 同时填充和描边
};
void setStyle(Style style);
// 描边属性
void setStrokeWidth(SkScalar width);
void setStrokeCap(Cap cap); // Butt, Round, Square
void setStrokeJoin(Join join); // Miter, Round, Bevel
void setStrokeMiter(SkScalar miter);
// 抗锯齿
void setAntiAlias(bool aa);
void setDither(bool dither);
// 着色器(渐变、图案等)
void setShader(sk_sp<SkShader> shader);
// 图像滤镜
void setImageFilter(sk_sp<SkImageFilter> imageFilter);
// 颜色滤镜
void setColorFilter(sk_sp<SkColorFilter> colorFilter);
// 遮罩滤镜(模糊)
void setMaskFilter(sk_sp<SkMaskFilter> maskFilter);
// 路径效果(虚线、圆角等)
void setPathEffect(sk_sp<SkPathEffect> pathEffect);
// 混合模式
void setBlendMode(SkBlendMode mode);
void setBlender(sk_sp<SkBlender> blender); // 自定义混合
};
使用示例:
SkPaint paint;
paint.setColor(SK_ColorRED); // 红色
paint.setStyle(SkPaint::kStroke_Style); // 描边模式
paint.setStrokeWidth(5.0f); // 5像素宽度
paint.setStrokeCap(SkPaint::kRound_Cap); // 圆形端点
paint.setAntiAlias(true); // 抗锯齿
paint.setShader(SkGradientShader::MakeLinear(...)); // 线性渐变
canvas->drawRect(rect, paint);
2. 路径系统
2.1 SkPath 矢量路径
位置: include/core/SkPath.h, src/core/SkPath.cpp
代码量: 3,910 行
路径构建:
class SkPath {
public:
// 移动与直线
SkPath& moveTo(SkScalar x, SkScalar y);
SkPath& lineTo(SkScalar x, SkScalar y);
SkPath& close(); // 闭合路径
// 曲线
SkPath& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); // 二次贝塞尔
SkPath& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar x3, SkScalar y3); // 三次贝塞尔
SkPath& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar w); // 圆锥曲线
// 弧线
SkPath& arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
bool forceMoveTo);
SkPath& arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar radius); // 圆角弧线
// 添加形状
SkPath& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW);
SkPath& addOval(const SkRect& oval, SkPathDirection dir = SkPathDirection::kCW);
SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius,
SkPathDirection dir = SkPathDirection::kCW);
SkPath& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kCW);
SkPath& addPoly(const SkPoint pts[], int count, bool close);
SkPath& addPath(const SkPath& src, SkScalar dx = 0, SkScalar dy = 0);
// 路径操作
enum FillType {
kWinding_FillType, // 非零环绕规则
kEvenOdd_FillType, // 奇偶规则
kInverseWinding_FillType, // 反转非零环绕
kInverseEvenOdd_FillType // 反转奇偶
};
void setFillType(FillType ft);
// 路径信息
bool isEmpty() const;
bool isConvex() const;
SkRect getBounds() const;
SkRect computeTightBounds() const;
// 变换
void transform(const SkMatrix& matrix, SkPath* dst = nullptr);
void offset(SkScalar dx, SkScalar dy, SkPath* dst = nullptr);
};
路径示例:
// 绘制圆角矩形
SkPath path;
path.addRoundRect(SkRect::MakeXYWH(10, 10, 100, 100), 10, 10);
canvas->drawPath(path, paint);
// 绘制复杂路径
SkPath complexPath;
complexPath.moveTo(50, 50);
complexPath.lineTo(100, 100);
complexPath.quadTo(150, 100, 150, 150); // 二次贝塞尔曲线
complexPath.cubicTo(150, 200, 100, 250, 50, 250); // 三次贝塞尔
complexPath.close();
canvas->drawPath(complexPath, paint);
2.2 路径运算 (PathOps)
位置: src/pathops/
文件数: 15+ 专用文件
布尔运算:
namespace SkPathOps {
enum Operation {
kDifference_Op, // A - B
kIntersect_Op, // A ∩ B
kUnion_Op, // A ∪ B
kXOR_Op, // A ⊕ B
kReverseDifference_Op // B - A
};
}
// 使用示例
SkPath path1, path2, result;
path1.addCircle(50, 50, 40);
path2.addCircle(70, 50, 40);
// 计算两圆的并集
Op(path1, path2, SkPathOps::kUnion_Op, &result);
canvas->drawPath(result, paint);
路径简化:
// 简化路径(移除冗余点)
SkPath simplified;
Simplify(originalPath, &simplified);
// 创建轮廓
SkPath outline;
AsWinding(originalPath, &outline);
3. 着色器系统
3.1 SkShader 基类
位置: include/core/SkShader.h
类型: 25+ 种着色器
着色器层次:
SkShader (基类)
├── SkColorShader - 纯色
├── SkImageShader - 图像平铺
├── SkPerlinNoiseShader - 柏林噪声
├── SkPictureShader - 图片着色器
├── SkLocalMatrixShader - 矩阵变换包装
├── 渐变着色器
│ ├── SkLinearGradient - 线性渐变
│ ├── SkRadialGradient - 径向渐变
│ ├── SkSweepGradient - 角度渐变
│ └── SkTwoPointConicalGradient - 双点圆锥渐变
└── RuntimeEffect - 自定义着色器(SkSL语言)
3.2 渐变着色器
线性渐变:
SkPoint points[2] = {{0, 0}, {100, 100}};
SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE};
sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
points, // 起点和终点
colors, // 颜色数组
nullptr, // 位置(nullptr表示均匀分布)
2, // 颜色数量
SkTileMode::kClamp // 平铺模式:Clamp, Repeat, Mirror, Decal
);
SkPaint paint;
paint.setShader(shader);
canvas->drawRect(rect, paint);
径向渐变:
SkColor colors[] = {SK_ColorYELLOW, SK_ColorGREEN, SK_ColorBLUE};
SkScalar positions[] = {0.0f, 0.5f, 1.0f};
sk_sp<SkShader> radialShader = SkGradientShader::MakeRadial(
SkPoint::Make(100, 100), // 中心点
50, // 半径
colors,
positions,
3,
SkTileMode::kClamp
);
双点圆锥渐变:
sk_sp<SkShader> conicalShader = SkGradientShader::MakeTwoPointConical(
SkPoint::Make(50, 50), // 起始圆心
20, // 起始半径
SkPoint::Make(150, 150), // 结束圆心
80, // 结束半径
colors,
positions,
colorCount,
SkTileMode::kRepeat
);
3.3 图像着色器
sk_sp<SkImage> image = SkImage::MakeFromEncoded(...);
sk_sp<SkShader> imageShader = image->makeShader(
SkTileMode::kRepeat, // 水平平铺
SkTileMode::kMirror, // 垂直镜像
SkSamplingOptions(SkFilterMode::kLinear) // 采样选项
);
SkPaint paint;
paint.setShader(imageShader);
canvas->drawRect(largeRect, paint); // 用图像平铺填充
3.4 运行时着色器 (SkSL)
SkSL (Skia Shading Language) - Skia的着色器语言:
// 定义SkSL着色器
const char* sksl = R"(
uniform float2 resolution;
uniform float time;
half4 main(float2 fragCoord) {
float2 uv = fragCoord / resolution;
float wave = sin(uv.x * 10.0 + time) * 0.5 + 0.5;
return half4(uv.x, wave, uv.y, 1.0);
}
)";
// 编译着色器
auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(sksl));
if (!effect) {
SkDebugf("Shader compilation error: %s\n", error.c_str());
return;
}
// 设置Uniform参数
SkRuntimeShaderBuilder builder(effect);
builder.uniform("resolution") = SkV2{800.0f, 600.0f};
builder.uniform("time") = currentTime;
sk_sp<SkShader> shader = builder.makeShader();
paint.setShader(shader);
4. 图像编解码系统
4.1 支持的格式
位置: src/codec/
文件数: 71 个编解码器文件
支持格式:
图像格式支持
├── JPEG (SkJpegCodec) - 50KB+
│ ├── 多图JPEG (SkJpegMultiPicture)
│ ├── XMP元数据 (SkJpegXmp)
│ └── 渐进式JPEG
├── PNG (SkPngCodec) - 45KB+
│ ├── 标准PNG
│ ├── 动画PNG (APNG)
│ └── 交错PNG
├── WebP (SkWebpCodec)
│ ├── 静态WebP
│ └── 动画WebP
├── BMP (SkBmpCodec)
│ ├── 标准BMP
│ ├── RLE压缩
│ └── 掩码格式
├── HEIF/HEIC (SkHeifCodec) - 新一代高效格式
├── AVIF (SkAvifCodec) - AV1图像格式
├── JPEG XL (SkJpegxlCodec) - 下一代JPEG
├── GIF (SkWuffsCodec) - 通过Wuffs解码器
├── WBMP (SkWbmpCodec) - 无线位图
├── ICO (SkIcoCodec) - Windows图标
└── RAW (SkRawCodec) - 数码相机RAW格式
4.2 解码流程
#include "include/codec/SkCodec.h"
// 从文件创建编解码器
sk_sp<SkData> data = SkData::MakeFromFileName("image.png");
std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data);
if (!codec) {
// 解码器创建失败
return;
}
// 获取图像信息
SkImageInfo info = codec->getInfo();
SkDebugf("Image: %dx%d, ColorType: %d\n",
info.width(), info.height(), info.colorType());
// 分配像素内存
SkBitmap bitmap;
bitmap.allocPixels(info);
// 解码图像
SkCodec::Result result = codec->getPixels(info, bitmap.getPixels(),
bitmap.rowBytes());
if (result == SkCodec::kSuccess) {
// 解码成功,使用bitmap
canvas->drawImage(bitmap.asImage(), 0, 0);
}
增量解码(适用于网络流):
std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(stream));
SkCodec::Options options;
options.fSubset = ⊂ // 部分解码
// 渐进式解码
for (int scanline = 0; scanline < height; scanline += step) {
options.fSubset->fTop = scanline;
options.fSubset->fBottom = std::min(scanline + step, height);
codec->getPixels(info, pixels, rowBytes, &options);
// 显示部分解码结果
}
4.3 Android编解码器
SkAndroidCodec - Android优化接口:
#include "include/codec/SkAndroidCodec.h"
std::unique_ptr<SkAndroidCodec> androidCodec =
SkAndroidCodec::MakeFromCodec(std::move(codec));
// 计算采样大小(节省内存)
SkISize desiredSize = {800, 600};
int sampleSize = androidCodec->computeSampleSize(&desiredSize);
SkAndroidCodec::AndroidOptions options;
options.fSampleSize = sampleSize;
// 解码为缩放后的图像
androidCodec->getAndroidPixels(scaledInfo, scaledPixels, rowBytes, &options);
5. 文本渲染系统
5.1 文本渲染管线
文本输入(UTF-8/UTF-16字符串)
↓
SkFont(字体属性:大小、倾斜、粗细)
↓
SkShaper(文本整形 - HarfBuzz)
├─→ 双向文本处理(BiDi)
├─→ 复杂脚本支持(阿拉伯语、印地语等)
├─→ 连字(ligature)
└─→ 字距调整(kerning)
↓
SkTextBlob(字形ID + 位置)
↓
GPU/CPU渲染
├─→ GPU: 距离场渲染(SDF)
└─→ CPU: 光栅化字形
↓
屏幕显示
5.2 SkFont 字体接口
位置: include/core/SkFont.h
class SkFont {
public:
// 字体设置
void setTypeface(sk_sp<SkTypeface> typeface);
void setSize(SkScalar textSize);
void setScaleX(SkScalar scaleX); // 水平缩放
void setSkewX(SkScalar skewX); // 倾斜(斜体)
// 渲染选项
void setEdging(Edging edging); // Alias, AntiAlias, SubpixelAntiAlias
void setHinting(SkFontHinting hinting); // None, Slight, Normal, Full
void setEmbolden(bool embolden); // 加粗
void setLinearMetrics(bool linearMetrics);
void setSubpixel(bool subpixel); // 亚像素定位
// 字形查询
int textToGlyphs(const void* text, size_t byteLength,
SkTextEncoding encoding, SkGlyphID glyphs[],
int maxGlyphCount) const;
SkScalar measureText(const void* text, size_t byteLength,
SkTextEncoding encoding, SkRect* bounds = nullptr) const;
};
使用示例:
SkFont font;
font.setTypeface(SkTypeface::MakeFromName("Roboto", SkFontStyle::Normal()));
font.setSize(24.0f);
font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
SkPaint paint;
paint.setColor(SK_ColorBLACK);
canvas->drawString("Hello Skia!", 100, 100, font, paint);
5.3 SkShaper 文本整形
位置: modules/skshaper/
依赖: HarfBuzz(文本整形引擎)
#include "modules/skshaper/include/SkShaper.h"
// 创建整形器
std::unique_ptr<SkShaper> shaper = SkShaper::Make();
// 文本整形
class TextBlobBuilderRunHandler : public SkShaper::RunHandler {
// 实现回调接口
// ...
};
TextBlobBuilderRunHandler handler(text, offset);
shaper->shape(text, textLength, font, bidiLevel, width, &handler);
sk_sp<SkTextBlob> textBlob = handler.makeBlob();
canvas->drawTextBlob(textBlob, x, y, paint);
复杂文本支持:
// 阿拉伯语(从右到左)
const char* arabicText = "مرحبا"; // "Hello"
// 印地语(天城文)
const char* hindiText = "नमस्ते"; // "Namaste"
// HarfBuzz自动处理连字、变音符号、从右到左等
shaper->shape(arabicText, strlen(arabicText), font, 1, width, &handler);
5.4 GPU文本渲染
距离场文本 (Signed Distance Field):
优势:
• 缩放无损失(矢量化效果)
• 内存效率高(低分辨率纹理)
• 抗锯齿质量好
• 支持描边、阴影效果
实现:
src/text/gpu/SDFMaskFilter.cpp
src/text/gpu/TextBlob.cpp
字形图集缓存:
src/text/gpu/StrikeCache.h
• LRU缓存字形纹理
• 避免重复渲染
• 批量上传GPU
6. GPU渲染后端 - Ganesh
6.1 Ganesh 架构
位置: src/gpu/ganesh/
文件数: 105+ 核心文件
架构层次:
┌─────────────────────────────────────┐
│ 上下文层 (Context Layer) │
│ GrDirectContext - 同步执行 │
│ GrRecordingContext - 延迟记录 │
│ GrDDLContext - 延迟显示列表 │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ 表面层 (Surface Layer) │
│ GrRenderTarget - 渲染目标 │
│ GrTexture - GPU纹理 │
│ SurfaceFillContext - 绘图上下文 │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ 操作层 (Operation Layer) │
│ GrOp - 绘制操作(50+类型) │
│ GrRenderTask - 任务调度 │
│ GrOpFlushState - 刷新状态 │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ GPU抽象层 (GPU Abstraction) │
│ GrGpu - GPU命令接口 │
│ GrCaps - 能力检测 │
│ GrResourceCache - 资源缓存 │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ 后端实现 (Backend) │
│ OpenGL (gl/) - 60+文件 │
│ Vulkan (vk/) - 规划中 │
│ Metal - 平台特定 │
│ Dawn (WebGPU) - 实验性 │
└─────────────────────────────────────┘
6.2 GrRecordingContext
延迟记录模式:
// 创建录制上下文
GrRecordingContext* recordingContext = ...;
// 录制绘制命令(不立即执行)
SkCanvas* canvas = surface->getCanvas();
canvas->clear(SK_ColorWHITE);
canvas->drawRect(...);
canvas->drawPath(...);
// 命令被记录为GrOp对象
// 刷新执行
recordingContext->flush(); // 提交到GPU
6.3 GrOp 操作系统
操作类型(50+ 种):
// 路径渲染操作
AAConvexPathOp // 抗锯齿凸路径
AAHairLinePathOp // 抗锯齿细线
AALinearizingConvexPathOp // 线性化凸路径
AtlasPathOp // 图集路径(缓存)
DashLinePathOp // 虚线路径
DefaultPathOp // 默认路径(镶嵌)
// 图元操作
ClearOp // 清除
DrawAtlasOp // 图集绘制
DrawVerticesOp // 顶点绘制
FillRectOp // 填充矩形
StrokeRectOp // 描边矩形
// 文本操作
AtlasTextOp // 图集文本
// 图像操作
TextureOp // 纹理绘制
CopyOp // 纹理复制
操作合并优化:
// 示例:批量绘制矩形
// 绘制100个矩形
for (int i = 0; i < 100; i++) {
canvas->drawRect(rects[i], paint);
}
// Ganesh自动合并为单个GrOp
// → 单次GPU绘制调用
// → 大幅减少CPU-GPU通信开销
6.4 资源管理
GrResourceCache - LRU缓存:
// 纹理缓存
GrResourceCache* cache = context->priv().resourceCache();
// 设置缓存大小
cache->setLimits(maxResources, maxBytes);
// 缓存策略
• 频繁使用的纹理保留
• 长时间未使用的纹理释放
• 内存压力时主动清理
缓存的资源类型:
- GPU纹理(GrTexture)
- 渲染目标(GrRenderTarget)
- 顶点缓冲区(GrBuffer)
- 着色器程序(GrGLProgram)
- 路径图集(AtlasPathRenderer)
6.5 OpenGL后端
位置: src/gpu/ganesh/gl/
文件数: 60+ 文件
核心组件:
GrGLCaps.cpp (244KB)
• 检测OpenGL版本(ES 2.0, 3.0, Desktop GL)
• 查询扩展支持(EXT_*, ARB_*, OES_*)
• 纹理格式能力矩阵
• 帧缓冲支持检测
• MSAA能力检测
GrGLGpu.cpp
• OpenGL命令生成
• 状态缓存与优化
• 纹理上传下载
• 帧缓冲操作
GrGLTexture.cpp/h
• 纹理对象封装
• Mipmap管理
• 采样器设置
GrGLBuffer.cpp/h
• VBO/IBO管理
• 动态缓冲区池化
着色器生成:
GrGLSLProgramBuilder
• 根据GrOp生成GLSL代码
• 顶点着色器 + 片段着色器
• Uniform变量管理
• 编译链接缓存
7. 软件渲染后端
7.1 SkBlitter 系统
位置: src/core/SkBlitter*.cpp
作用: 像素级混合与光栅化
Blitter层次:
SkBlitter (基类)
├── SkBlitter_ARGB32 (53KB) - 32位ARGB格式
│ ├── 支持29种混合模式
│ ├── Alpha混合优化
│ └── SIMD加速
├── SkBlitter_A8 - 8位Alpha遮罩
├── SkBlitter_Sprite - 精灵/位图复制
├── SkBlitter_RGB565 - 16位RGB格式
└── 自定义Blitter(裁剪、着色器等)
扫描线光栅化:
// 路径光栅化流程
SkPath → 边缘列表(SkAAClip, SkAnalyticEdge)
→ 扫描线填充
→ Blitter混合像素
→ 帧缓冲内存
示例:32位ARGB混合:
// SkBlitter_ARGB32.cpp 核心逻辑
void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) {
SkPMColor* device = fDevice.writable_addr32(x, y);
SkPMColor color = fPMColor;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
device[j] = blend(device[j], color, fMode); // Alpha混合
}
device += fRowBytes >> 2; // 下一行
}
}
7.2 抗锯齿技术
SkAAClip - 抗锯齿裁剪:
src/core/SkAAClip.cpp
• 基于Alpha遮罩的裁剪
• 运行长度编码(RLE)压缩
• 支持复杂路径裁剪
• 内存效率高
SkAnalyticEdge - 解析边缘:
src/core/SkAnalyticEdge.cpp
• 解析边缘光栅化
• 亚像素精度
• 高质量抗锯齿
• CPU密集型,质量优先
7.3 SIMD优化
位置: src/opts/
优化架构:
SIMD优化
├── SSE2, SSSE3, SSE4.2 (x86/x64)
├── AVX, AVX2 (现代x86)
├── NEON (ARM, ARM64)
└── CRC32 (ARM64特定)
优化的操作:
- 像素混合(Alpha blending)
- 颜色空间转换
- 图像缩放
- 模糊滤镜
- 矩阵运算
示例:
// src/opts/SkBlitRow_opts.h
void S32A_Opaque_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
const SkPMColor* SK_RESTRICT src,
int count, U8CPU alpha) {
// SSE2 SIMD指令实现
__m128i src_pixel = _mm_loadu_si128((__m128i*)src);
// 4像素并行处理
// ...
}
渲染管线
软件渲染路径
SkCanvas::drawPath(path, paint)
↓
SkBitmapDevice::drawPath() // 软件设备
↓
路径简化与裁剪
├─→ 应用当前裁剪区域
└─→ 应用变换矩阵
↓
SkScan::FillPath() // 扫描线填充
↓
边缘生成
├─→ SkAAClip(抗锯齿)
└─→ SkAnalyticEdge(解析边缘)
↓
Blitter选择
├─→ SkBlitter_ARGB32(32位)
├─→ SkBlitter_A8(Alpha)
└─→ SkBlitter_RGB565(16位)
↓
扫描线遍历
↓
Alpha混合(29种混合模式)
├─→ Normal, Multiply, Screen
├─→ Overlay, Darken, Lighten
└─→ Color-dodge, Color-burn, etc.
↓
写入帧缓冲内存
GPU渲染路径(Ganesh)
SkCanvas::drawPath(path, paint)
↓
GrRecordingContext::recordingContext()
↓
GrSurfaceDrawContext::drawPath()
↓
路径渲染器选择
├─→ AAConvexPathRenderer(凸路径)
├─→ AAHairLinePathRenderer(细线)
├─→ AtlasPathRenderer(缓存路径)
├─→ DefaultPathRenderer(镶嵌)
└─→ DashLinePathRenderer(虚线)
↓
创建GrOp(绘制操作)
↓
GrRenderTask调度
├─→ 依赖分析
├─→ 操作合并(批处理)
└─→ 资源分配
↓
GrOpFlushState::flush()
↓
GrGpu命令生成
├─→ 设置渲染状态
├─→ 绑定纹理/缓冲区
├─→ 上传顶点数据
└─→ 生成着色器程序
↓
后端驱动调用
├─→ OpenGL: glDrawArrays/Elements
├─→ Vulkan: vkCmdDraw*
└─→ Metal: MTLRenderCommandEncoder
↓
GPU执行
├─→ 顶点着色器
├─→ 光栅化
├─→ 片段着色器
└─→ 混合/深度测试
↓
帧缓冲(GPU显存)
渲染路径选择
决策因素:
if (surface.isGpuBacked()) {
// GPU渲染
if (path.isConvex() && paint.isAntiAlias()) {
use AAConvexPathRenderer; // 快速凸路径
} else if (hasCachedAtlas(path)) {
use AtlasPathRenderer; // 缓存路径
} else {
use DefaultPathRenderer; // 通用镶嵌
}
} else {
// 软件渲染
use SkScan + SkBlitter;
}
构建系统
Android.bp 配置
位置: external/skia/Android.bp
大小: ~197 KB
主要配置块:
// 许可证定义
license {
name: "external_skia_license",
license_kinds: ["SPDX-license-identifier-BSD"],
}
// 架构特定源文件
filegroup {
name: "skia_srcs_arm",
srcs: [
"src/core/SkCpu.cpp",
"src/opts/SkBitmapProcState_opts.cpp",
// ARM NEON优化文件
],
}
filegroup {
name: "skia_srcs_arm64",
srcs: [
"src/opts/SkChecksum_opts.h", // CRC32指令
"src/opts/SkRasterPipeline_opts.h",
],
}
filegroup {
name: "skia_srcs_x86",
srcs: [
"src/opts/SkBlitRow_opts_SSE4.cpp",
"src/opts/SkOpts_avx.cpp",
"src/opts/SkOpts_hsw.cpp", // Haswell AVX2
"src/opts/SkOpts_skx.cpp", // Skylake-X
],
}
// 主库定义
cc_library {
name: "libskia",
host_supported: true,
cflags: [
"-DSK_GANESH", // 启用Ganesh GPU后端
"-DSK_ENABLE_SKSL", // 启用SkSL着色器语言
"-DSK_GL", // OpenGL支持
"-DSK_ENABLE_PRECOMPILE", // 预编译支持
"-DSK_GAMMA_APPLY_TO_A8", // Gamma校正
"-DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=0",
"-DSK_DISABLE_LEGACY_SHADERCONTEXT",
"-DSK_DISABLE_LOWP_RASTER_PIPELINE",
"-DSK_FORCE_AAA", // 强制解析抗锯齿
"-DSK_SUPPORT_GPU=1", // GPU支持
"-DSK_R32_SHIFT=16", // 32位颜色位移
],
srcs: [
"src/codec/*.cpp",
"src/core/*.cpp",
"src/effects/*.cpp",
"src/gpu/ganesh/**/*.cpp",
"src/image/*.cpp",
"src/pathops/*.cpp",
"src/shaders/*.cpp",
"src/text/*.cpp",
"src/ports/SkMemory_malloc.cpp",
// 3000+ 源文件
],
arch: {
arm: {
srcs: [":skia_srcs_arm"],
neon: {
srcs: [":skia_srcs_arm_neon"],
},
},
arm64: {
srcs: [":skia_srcs_arm64"],
},
x86: {
srcs: [":skia_srcs_x86"],
},
x86_64: {
srcs: [":skia_srcs_x86"],
},
},
shared_libs: [
"libandroidicu", // Unicode支持
"libcutils", // Android工具库
"libEGL", // EGL接口
"libGLESv2", // OpenGL ES 2.0/3.0
"libheif", // HEIF解码
"libjpeg", // JPEG解码
"liblog", // Android日志
"libpng", // PNG解码
"libvulkan", // Vulkan接口
"libwebp-decode", // WebP解码
"libwebp-encode", // WebP编码
],
static_libs: [
"libarect", // 矩形工具
"libdng_sdk", // RAW图像
"libexpat", // XML解析(SVG)
"libft2", // FreeType2字体
"libsfntly", // SFNT字体解析
"libskqp_harfbuzz", // HarfBuzz文本整形
"libwuffs_mirror_release_c", // Wuffs解码器
],
export_include_dirs: [
"include",
"include/android",
],
}
// RenderEngine集成库
cc_library {
name: "libskia_renderengine",
defaults: ["skia_renderengine_deps"],
srcs: [
"renderengine/RenderEngine.cpp",
"renderengine/SkiaGLRenderEngine.cpp",
"renderengine/SkiaVkRenderEngine.cpp",
],
shared_libs: [
"libskia",
"libtonemap", // HDR色调映射
],
}
编译选项详解
功能启用:
-DSK_GANESH // Ganesh GPU后端
-DSK_GRAPHITE // Graphite新GPU API(实验性)
-DSK_ENABLE_SKSL // SkSL着色器语言
-DSK_GL // OpenGL/ES支持
-DSK_VULKAN // Vulkan支持
-DSK_METAL // Metal支持(iOS/macOS)
性能优化:
-DSK_FORCE_AAA // 强制解析抗锯齿(高质量)
-DSK_DISABLE_LOWP_RASTER_PIPELINE // 禁用低精度光栅管线
-DSK_ENABLE_PRECOMPILE // 预编译着色器
内存优化:
-DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=0 // 禁用静态全局初始化器
-DSK_DISABLE_LEGACY_SHADERCONTEXT // 移除旧版着色器上下文
平台特定:
-DSK_BUILD_FOR_ANDROID // Android平台
-DSK_R32_SHIFT=16 // 32位颜色R通道位移
BUILD.gn 配置
位置: external/skia/BUILD.gn
大小: ~98 KB
主要配置:
# 配置定义
config("skia_public") {
include_dirs = [ "include" ]
defines = [ "SK_GANESH", "SK_ENABLE_SKSL" ]
}
config("skia_private") {
include_dirs = [ "src" ]
defines = [
"SK_GAMMA_APPLY_TO_A8",
"SK_ALLOW_STATIC_GLOBAL_INITIALIZERS=0",
]
}
# 主库目标
component("skia") {
public_configs = [ ":skia_public" ]
configs += [ ":skia_private" ]
sources = [
# 3000+ 源文件
]
deps = [
"//third_party/freetype2",
"//third_party/harfbuzz",
"//third_party/libjpeg-turbo",
"//third_party/libpng",
"//third_party/libwebp",
"//third_party/zlib",
]
}
编译产物
共享库:
- libskia.so: 主Skia库(~10-15 MB)
- libskia_renderengine.so: RenderEngine集成(~500 KB)
- libskparagraph.so: 段落布局(~1 MB)
- libskshaper.so: 文本整形(~500 KB)
位置:
/system/lib64/libskia.so(64位)/system/lib/libskia.so(32位)
Android集成
HWUI集成
位置: frameworks/base/libs/hwui/pipeline/skia/
集成层次:
Android View系统
↓
HWUI (Hardware UI)
├─→ SkiaOpenGLPipeline - OpenGL渲染管线
├─→ SkiaVulkanPipeline - Vulkan渲染管线
├─→ SkiaDisplayList - 延迟绘制列表
└─→ LayerDrawable - 图层绘制
↓
Skia库
├─→ SkCanvas API
└─→ Ganesh GPU后端
↓
GPU驱动 (OpenGL ES / Vulkan)
关键文件:
SkiaOpenGLPipeline.cpp/h:
class SkiaOpenGLPipeline : public SkiaPipeline {
public:
void renderFrame(const LayerUpdateQueue& layers,
const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes,
FrameInfoVisualizer* profiler);
void setSurface(ANativeWindow* surface);
bool swapBuffers();
private:
sk_sp<GrDirectContext> mGrContext; // GPU上下文
sk_sp<SkSurface> mSurface; // 渲染表面
};
SkiaDisplayList.cpp:
class SkiaDisplayList : public DisplayList {
public:
// 延迟记录绘制命令
void draw(SkCanvas* canvas) override;
// 优化合并操作
void optimizeOperations();
private:
sk_sp<SkPicture> mPicture; // Skia绘制记录
};
TransformCanvas.cpp - Canvas变换封装:
class TransformCanvas : public SkCanvas {
public:
// 拦截变换操作
void translate(SkScalar dx, SkScalar dy) override;
void scale(SkScalar sx, SkScalar sy) override;
void rotate(SkScalar degrees) override;
// 应用硬件加速变换
void applyHardwareTransform();
};
RenderEngine集成
位置: external/skia/renderengine/
用途: SurfaceFlinger的合成引擎
架构:
SurfaceFlinger(系统合成器)
↓
RenderEngine接口
├─→ SkiaGLRenderEngine - OpenGL实现
└─→ SkiaVkRenderEngine - Vulkan实现
↓
Skia渲染
↓
显示硬件
RenderEngine.cpp:
class SkiaGLRenderEngine : public RenderEngine {
public:
// 绘制图层
void drawLayers(const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
const sp<GraphicBuffer>& buffer);
// 色调映射(HDR)
void mapColors(const DisplaySettings& display);
private:
sk_sp<GrDirectContext> mGrContext;
sk_sp<SkSurface> mSurface;
};
Canvas API桥接
Java层:
// android.graphics.Canvas
public class Canvas {
private long mNativeCanvasWrapper; // Native SkCanvas指针
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
nativeDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
}
private static native void nativeDrawRect(long nativeCanvas, float left, float top,
float right, float bottom, long nativePaint);
}
JNI层 (frameworks/base/core/jni/android_graphics_Canvas.cpp):
static void drawRect(JNIEnv* env, jobject, jlong canvasHandle,
jfloat left, jfloat top, jfloat right, jfloat bottom,
jlong paintHandle) {
SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
canvas->drawRect(rect, *paint);
}
static const JNINativeMethod gCanvasMethods[] = {
{"nativeDrawRect", "(JFFFFJJ)V", (void*)drawRect},
// 其他绘图方法...
};
内存追踪
SkiaMemoryTracer.cpp/h:
class SkiaMemoryTracer : public SkTraceMemoryDump {
public:
// 追踪GPU资源
void dumpNumericValue(const char* dumpName,
const char* valueName,
const char* units,
uint64_t value) override;
// 追踪纹理内存
void setMemoryBacking(const char* dumpName,
const char* backingType,
const char* backingObjectId) override;
// 输出到ATrace
void logToATrace();
};
性能分析
ATraceMemoryDump.cpp/h:
// 集成Android Trace工具
ATRACE_NAME("Skia::drawPath");
ATRACE_INT("Skia::GPU::TextureMemoryMB", textureMemory / 1024 / 1024);
// 分析绘制性能
void SkiaOpenGLPipeline::renderFrame(...) {
ATRACE_CALL();
{
ATRACE_NAME("Skia::recordDisplayLists");
// 记录绘制列表
}
{
ATRACE_NAME("Skia::flushGpuCommands");
mGrContext->flush();
}
{
ATRACE_NAME("Skia::swapBuffers");
swapBuffers();
}
}
性能优化
1. GPU加速优化
1.1 纹理图集(Atlasing)
AtlasPathRenderer.cpp - 路径图集缓存:
原理:
• 预渲染常用路径到大纹理
• 绘制时直接复制纹理区域
• 减少GPU状态切换
优势:
• 相同路径绘制速度提升10-100倍
• 减少绘制调用(Draw Call)
• 降低CPU-GPU通信开销
实现:
1. 路径哈希判断是否已缓存
2. 未缓存则分配图集空间
3. 渲染路径到图集纹理
4. 后续绘制直接引用图集坐标
AtlasTextOp.cpp - 文本字形图集:
class TextBlob {
// 字形图集坐标
struct GlyphAtlasEntry {
uint16_t atlasX, atlasY; // 图集中位置
uint16_t width, height; // 字形尺寸
int16_t bearingX, bearingY; // 基线偏移
};
// 批量绘制文本
void draw(GrRenderTargetContext* rtc) {
// 单次绘制调用渲染多个字形
drawAtlasQuads(glyphPositions, atlasCoords, count);
}
};
1.2 资源缓存策略
GrResourceCache - LRU缓存:
class GrResourceCache {
public:
// 设置缓存限制
void setLimits(int maxCount, size_t maxBytes);
// 缓存策略
void purgeAsNeeded() {
// 1. 移除未使用且最久未访问的资源
// 2. 优先保留:
// - 最近绘制的纹理
// - 屏幕可见区域的资源
// - 动画相关资源
}
private:
// LRU双向链表
SkTDArray<GrGpuResource*> fPurgeableQueue;
size_t fBudgetedBytes; // 已使用字节数
size_t fMaxBytes; // 最大字节数
};
缓存资源类型:
GPU资源缓存
├── 纹理 (GrTexture)
│ ├── 图像纹理
│ ├── 字形图集
│ └── 路径图集
├── 渲染目标 (GrRenderTarget)
├── 顶点缓冲区 (GrBuffer)
│ ├── VBO缓冲池
│ └── IBO缓冲池
└── 程序对象 (GrGLProgram)
└── 着色器编译缓存
1.3 批量渲染
GrOpFlushState - 操作合并:
// 示例:绘制100个矩形
for (int i = 0; i < 100; i++) {
SkPaint paint;
paint.setColor(colors[i]);
canvas->drawRect(rects[i], paint);
}
// Ganesh自动合并优化:
// 1. 识别相同渲染状态的操作
// 2. 合并为单个GrOp
// 3. 单次GPU调用绘制所有矩形
// 优化前:100次glDrawArrays调用
// 优化后:1次glDrawArrays调用(Instancing)
顶点缓冲池化:
class GrBufferAllocPool {
// 复用顶点缓冲区
void* makeSpace(size_t size, GrBuffer** buffer, size_t* offset) {
if (currentBuffer->hasSpaceFor(size)) {
return currentBuffer->allocate(size); // 复用
} else {
return allocateNewBuffer(size); // 分配新缓冲区
}
}
};
2. CPU优化
2.1 SIMD优化
位置: src/opts/
SSE4.2优化 (x86/x64):
// SkBlitRow_opts_SSE4.cpp
void S32A_Opaque_BlitRow32_SSE42(SkPMColor* dst, const SkPMColor* src, int count) {
while (count >= 4) {
// 并行处理4个像素
__m128i src_pixel = _mm_loadu_si128((__m128i*)src);
__m128i dst_pixel = _mm_loadu_si128((__m128i*)dst);
// SIMD Alpha混合
__m128i result = blend_4pixels_sse(src_pixel, dst_pixel);
_mm_storeu_si128((__m128i*)dst, result);
src += 4;
dst += 4;
count -= 4;
}
// 处理剩余像素
while (count-- > 0) {
*dst++ = blend_pixel(*src++, *dst);
}
}
NEON优化 (ARM/ARM64):
// SkOpts_neon.cpp
void blit_row_s32a_opaque_neon(SkPMColor* dst, const SkPMColor* src, int count) {
while (count >= 8) {
// 并行处理8个像素(NEON 128位寄存器,8个32位像素分2次)
uint32x4_t src1 = vld1q_u32((uint32_t*)src);
uint32x4_t src2 = vld1q_u32((uint32_t*)src + 4);
uint32x4_t dst1 = vld1q_u32((uint32_t*)dst);
uint32x4_t dst2 = vld1q_u32((uint32_t*)dst + 4);
// NEON Alpha混合
uint32x4_t result1 = blend_neon(src1, dst1);
uint32x4_t result2 = blend_neon(src2, dst2);
vst1q_u32((uint32_t*)dst, result1);
vst1q_u32((uint32_t*)dst + 4, result2);
src += 8;
dst += 8;
count -= 8;
}
}
性能提升:
操作 标量 SSE4.2 NEON 提升倍数
Alpha混合 100 ms 25 ms 20 ms 4-5x
颜色转换 80 ms 15 ms 12 ms 5-6x
模糊滤镜 200 ms 40 ms 35 ms 5-6x
2.2 内存池化
SkArenaAlloc - 快速临时分配:
class SkArenaAlloc {
// 块分配,减少malloc调用
void* alloc(size_t size, size_t align) {
if (fCursor + size <= fEnd) {
void* ptr = fCursor;
fCursor += (size + align - 1) & ~(align - 1);
return ptr; // 快速路径:O(1)
}
return allocSlowPath(size, align); // 慢速路径:分配新块
}
// 析构时一次性释放所有内存
~SkArenaAlloc() {
freeAllBlocks();
}
};
使用场景:
- 路径临时数据
- 着色器中间结果
- 文本整形缓冲区
- 绘制命令记录
优势:
- 分配速度:比malloc快10-100倍
- 内存碎片:零碎片
- 释放开销:零开销(批量释放)
2.3 路径简化缓存
class SkPath {
// 缓存简化后的路径
mutable sk_sp<SkPath> fSimplifiedPath;
const SkPath& getSimplified() const {
if (!fSimplifiedPath) {
fSimplifiedPath = simplify(this); // 计算并缓存
}
return *fSimplifiedPath;
}
};
3. 延迟渲染
SkPicture - 绘制命令录制:
// 录制绘制命令
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(bounds);
canvas->drawRect(...);
canvas->drawPath(...);
canvas->drawText(...);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
// 多次重放(不重复计算)
for (int i = 0; i < 10; i++) {
picture->playback(canvas); // 快速重放
}
GrDeferredDisplayList - GPU延迟显示列表:
// 在工作线程录制
SkDeferredDisplayListRecorder recorder(characterization);
SkCanvas* canvas = recorder.getCanvas();
canvas->drawComplexScene(); // 录制GPU命令
sk_sp<SkDeferredDisplayList> ddl = recorder.detach();
// 在主线程提交到GPU
surface->draw(ddl); // 快速提交
优势:
- CPU/GPU并行工作
- 多线程录制绘制命令
- 减少主线程阻塞
- 提高帧率稳定性
4. 智能缓存
4.1 字形缓存
Strike Cache (文本渲染):
class SkStrikeCache {
// 字形度量缓存
struct GlyphMetrics {
SkRect bounds; // 边界框
SkPoint advance; // 前进宽度
uint8_t* image; // 光栅化图像
};
// LRU缓存查找
GlyphMetrics* findGlyph(SkGlyphID glyphID, const SkFont& font) {
StrikeKey key(glyphID, font);
if (cache.contains(key)) {
return cache.get(key); // 命中
}
// 未命中:光栅化字形
GlyphMetrics* metrics = rasterizeGlyph(glyphID, font);
cache.insert(key, metrics);
return metrics;
}
};
4.2 位图缓存
class SkBitmapCache {
// 缩放后的位图缓存
static bool Find(const SkBitmap& src, SkScalar scaleX, SkScalar scaleY,
SkBitmap* result) {
CacheKey key(src.getGenerationID(), scaleX, scaleY);
return cache.find(key, result);
}
static void Add(const SkBitmap& scaled, const SkBitmap& src,
SkScalar scaleX, SkScalar scaleY) {
CacheKey key(src.getGenerationID(), scaleX, scaleY);
cache.insert(key, scaled);
}
};
5. 预编译着色器
// GrGLGpu.cpp
class GrGLProgramCache {
// 着色器编译缓存
std::unordered_map<ProgramKey, GrGLProgram*> fCache;
GrGLProgram* findOrCreateProgram(const ProgramDesc& desc) {
ProgramKey key(desc);
auto it = fCache.find(key);
if (it != fCache.end()) {
return it->second; // 命中缓存
}
// 编译新着色器(耗时操作)
GrGLProgram* program = compileProgram(desc);
fCache[key] = program;
return program;
}
};
预编译时机:
- 应用启动时预编译常用着色器
- 首次绘制时编译并缓存
- 持久化缓存到磁盘(Android Shader Cache)
高级特性
1. 混合模式
29种混合模式 (include/core/SkBlendMode.h):
enum class SkBlendMode {
kClear, // 0
kSrc, // src
kDst, // dst
kSrcOver, // src + (1-src_alpha)*dst (默认)
kDstOver, // dst + (1-dst_alpha)*src
kSrcIn, // src * dst_alpha
kDstIn, // dst * src_alpha
kSrcOut, // src * (1-dst_alpha)
kDstOut, // dst * (1-src_alpha)
kSrcATop, // src*dst_alpha + dst*(1-src_alpha)
kDstATop, // dst*src_alpha + src*(1-dst_alpha)
kXor, // src*(1-dst_alpha) + dst*(1-src_alpha)
kPlus, // min(src + dst, 1)
kModulate, // src * dst
kScreen, // src + dst - src*dst
kOverlay, // 覆盖
kDarken, // min(src, dst)
kLighten, // max(src, dst)
kColorDodge, // 颜色减淡
kColorBurn, // 颜色加深
kHardLight, // 强光
kSoftLight, // 柔光
kDifference, // abs(dst - src)
kExclusion, // dst + src - 2*dst*src
kMultiply, // src * dst
kHue, // 色相
kSaturation, // 饱和度
kColor, // 颜色
kLuminosity, // 明度
};
使用示例:
SkPaint paint;
paint.setBlendMode(SkBlendMode::kMultiply); // 正片叠底
canvas->drawRect(rect, paint);
paint.setBlendMode(SkBlendMode::kScreen); // 滤色
canvas->drawCircle(x, y, radius, paint);
自定义混合器:
sk_sp<SkBlender> customBlender = SkBlenders::Arithmetic(
0.5f, // k1
0.5f, // k2
0.0f, // k3
0.0f, // k4
false // enforcePremul
);
// result = k1*src*dst + k2*src + k3*dst + k4
paint.setBlender(customBlender);
2. 图像滤镜
30+ 滤镜类型 (include/effects/SkImageFilters.h):
2.1 模糊滤镜
// 高斯模糊
sk_sp<SkImageFilter> blurFilter = SkImageFilters::Blur(
10.0f, // sigmaX
10.0f, // sigmaY
SkTileMode::kClamp,
nullptr // input
);
paint.setImageFilter(blurFilter);
canvas->drawBitmap(bitmap, 0, 0, &paint);
2.2 光照滤镜
// 点光源
SkPoint3 location = {100, 100, 50};
SkPoint3 target = {0, 0, 0};
SkColor lightColor = SK_ColorWHITE;
sk_sp<SkImageFilter> lightingFilter = SkImageFilters::PointLitDiffuse(
location,
lightColor,
1.0f, // surfaceScale
0.5f, // kd (漫反射常数)
nullptr
);
paint.setImageFilter(lightingFilter);
2.3 形态学滤镜
// 膨胀(Dilate)
sk_sp<SkImageFilter> dilateFilter = SkImageFilters::Dilate(
5, // radiusX
5, // radiusY
nullptr
);
// 腐蚀(Erode)
sk_sp<SkImageFilter> erodeFilter = SkImageFilters::Erode(
3, // radiusX
3, // radiusY
nullptr
);
2.4 滤镜组合
// 滤镜链
sk_sp<SkImageFilter> blur = SkImageFilters::Blur(5, 5, nullptr);
sk_sp<SkImageFilter> offset = SkImageFilters::Offset(10, 10, blur);
sk_sp<SkImageFilter> colorMatrix = SkImageFilters::ColorFilter(
SkColorFilters::Matrix(...), offset
);
paint.setImageFilter(colorMatrix);
3. 路径效果
SkPathEffect - 路径变换:
3.1 虚线效果
// 创建虚线
SkScalar intervals[] = {10.0f, 5.0f}; // 10px线段,5px间隔
sk_sp<SkPathEffect> dashEffect = SkDashPathEffect::Make(
intervals,
2, // 间隔数组长度
0.0f // 相位偏移
);
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setPathEffect(dashEffect);
canvas->drawLine(0, 100, 500, 100, paint); // 虚线
3.2 圆角效果
// 路径圆角
sk_sp<SkPathEffect> cornerEffect = SkCornerPathEffect::Make(20.0f); // 20px半径
paint.setPathEffect(cornerEffect);
canvas->drawPath(sharpPath, paint); // 尖角变圆角
3.3 离散效果
// 路径随机扰动
sk_sp<SkPathEffect> discreteEffect = SkDiscretePathEffect::Make(
10.0f, // segLength - 分段长度
4.0f // deviation - 偏移量
);
paint.setPathEffect(discreteEffect);
canvas->drawPath(path, paint); // 手绘风格
3.4 效果组合
// 组合多个路径效果
sk_sp<SkPathEffect> dash = SkDashPathEffect::Make(intervals, 2, 0);
sk_sp<SkPathEffect> corner = SkCornerPathEffect::Make(10);
// 先应用圆角,再应用虚线
sk_sp<SkPathEffect> combined = SkPathEffect::MakeCompose(dash, corner);
paint.setPathEffect(combined);
4. 颜色空间管理
SkColorSpace - 色彩管理:
// sRGB色彩空间
sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
// Display P3色彩空间(广色域)
sk_sp<SkColorSpace> displayP3 = SkColorSpace::MakeRGB(
SkNamedTransferFn::kSRGB,
SkNamedGamut::kDisplayP3
);
// 自定义ICC配置文件
sk_sp<SkData> iccData = SkData::MakeFromFileName("profile.icc");
sk_sp<SkColorSpace> custom = SkColorSpace::MakeICC(iccData->data(), iccData->size());
// 创建带色彩空间的图像
SkImageInfo info = SkImageInfo::Make(
width, height,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
displayP3 // 广色域
);
色彩空间转换:
// 转换图像色彩空间
sk_sp<SkImage> srgbImage = ...;
sk_sp<SkImage> p3Image = srgbImage->makeColorSpace(displayP3);
// 绘制时自动转换
canvas->drawImage(p3Image, 0, 0); // Skia自动转换到Canvas色彩空间
5. PDF渲染
#include "include/docs/SkPDFDocument.h"
// 创建PDF文档
SkDynamicMemoryWStream stream;
sk_sp<SkDocument> pdfDocument = SkPDF::MakeDocument(&stream);
// 添加页面
SkCanvas* pdfCanvas = pdfDocument->beginPage(612, 792); // Letter尺寸
pdfCanvas->drawRect(...);
pdfCanvas->drawText(...);
pdfDocument->endPage();
// 添加更多页面
pdfCanvas = pdfDocument->beginPage(612, 792);
// ...
pdfDocument->endPage();
// 完成文档
pdfDocument->close();
// 保存PDF
sk_sp<SkData> pdfData = stream.detachAsData();
writeToFile("output.pdf", pdfData);
6. 动画支持
SkLottie - After Effects动画:
#include "modules/skottie/include/Skottie.h"
// 加载Lottie JSON
sk_sp<skottie::Animation> animation = skottie::Animation::MakeFromFile("anim.json");
// 渲染动画帧
void onDraw(SkCanvas* canvas, double timeInSeconds) {
double progress = fmod(timeInSeconds, animation->duration()) / animation->duration();
animation->seek(progress); // 定位到时间点
animation->render(canvas, nullptr);
}
测试基础设施
测试组织
位置: tests/
文件数: 4,000+ 测试文件
测试分类:
tests/
├── Core Tests (核心功能)
│ ├── AAClipTest.cpp - 抗锯齿裁剪
│ ├── BlendTest.cpp - 混合模式
│ ├── BlurTest.cpp - 模糊滤镜
│ ├── CanvasTest.cpp - Canvas API
│ ├── PathTest.cpp - 路径操作
│ └── MatrixTest.cpp - 矩阵变换
├── GPU Tests (GPU渲染)
│ ├── GrContextTest.cpp
│ ├── GrTextureTest.cpp
│ └── GrOpTest.cpp
├── Codec Tests (编解码)
│ ├── CodecTest.cpp
│ ├── JpegTest.cpp
│ └── WebpTest.cpp
├── Image Tests (图像处理)
│ ├── ImageTest.cpp
│ ├── ImageFilterTest.cpp
│ └── ShaderTest.cpp
└── PathOps Tests (路径运算)
├── PathOpsTest.cpp
└── PathOpsCubicTest.cpp
单元测试框架
// tests/AAClipTest.cpp
#include "tests/Test.h"
DEF_TEST(AAClip_EmptyPath, reporter) {
SkAAClip clip;
SkPath emptyPath;
REPORTER_ASSERT(reporter, clip.setPath(emptyPath, nullptr, true));
REPORTER_ASSERT(reporter, clip.isEmpty());
}
DEF_TEST(AAClip_Bounds, reporter) {
SkAAClip clip;
SkIRect bounds = SkIRect::MakeWH(100, 100);
clip.setRect(bounds);
REPORTER_ASSERT(reporter, clip.getBounds() == bounds);
}
GPU测试
// tests/GrContextTest.cpp
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrContext_MaxTextureSize, reporter, ctxInfo) {
GrDirectContext* context = ctxInfo.directContext();
int maxSize = context->maxTextureSize();
REPORTER_ASSERT(reporter, maxSize > 0);
REPORTER_ASSERT(reporter, maxSize >= 2048); // OpenGL ES 2.0最小要求
}
DEF_GPUTEST_FOR_ALL_CONTEXTS(GrContext_Flush, reporter, ctxInfo) {
GrDirectContext* context = ctxInfo.directContext();
// 测试flush操作
GrFlushInfo flushInfo;
context->flush(flushInfo);
// 测试同步
bool result = context->submit(true);
REPORTER_ASSERT(reporter, result);
}
性能基准测试
位置: bench/
// bench/PathBench.cpp
class PathIterBench : public Benchmark {
public:
PathIterBench() {
// 构建测试路径
fPath.moveTo(0, 0);
for (int i = 0; i < 1000; i++) {
fPath.lineTo(i, i);
}
}
const char* onGetName() override { return "path_iter"; }
void onDraw(int loops, SkCanvas*) override {
for (int i = 0; i < loops; i++) {
SkPath::Iter iter(fPath, false);
SkPoint pts[4];
while (iter.next(pts) != SkPath::kDone_Verb) {
// 迭代路径段
}
}
}
private:
SkPath fPath;
};
DEF_BENCH(return new PathIterBench;)
金标准测试(Golden Tests)
// 渲染结果与预期图像对比
bool compareWithGolden(const SkBitmap& rendered, const char* goldenPath) {
sk_sp<SkImage> golden = SkImage::MakeFromEncoded(
SkData::MakeFromFileName(goldenPath)
);
// 像素级比较
return bitmaps_equal(rendered, golden->asLegacyBitmap());
}
DEF_TEST(RenderTest_GoldenComparison, reporter) {
SkBitmap bitmap;
bitmap.allocN32Pixels(100, 100);
SkCanvas canvas(bitmap);
// 渲染测试场景
drawTestScene(&canvas);
// 与金标准对比
REPORTER_ASSERT(reporter, compareWithGolden(bitmap, "test_golden.png"));
}
模糊测试(Fuzzing)
位置: fuzz/
// fuzz/FuzzCanvas.cpp
void fuzz_canvas(Fuzz* fuzz) {
SkBitmap bitmap;
bitmap.allocN32Pixels(128, 128);
SkCanvas canvas(bitmap);
while (!fuzz->exhausted()) {
uint8_t op;
fuzz->next(&op);
switch (op % 10) {
case 0: { // drawRect
SkRect rect;
fuzz->next(&rect);
SkPaint paint;
fuzz->next(&paint);
canvas.drawRect(rect, paint);
break;
}
case 1: { // drawPath
SkPath path;
fuzz->next(&path);
SkPaint paint;
fuzz->next(&paint);
canvas.drawPath(path, paint);
break;
}
// 更多操作...
}
}
}
DEF_FUZZ(canvas, fuzz) {
fuzz_canvas(fuzz);
}
最佳实践
1. 资源管理
使用智能指针:
// ✅ 推荐:使用sk_sp智能指针
sk_sp<SkImage> image = SkImage::MakeFromEncoded(data);
sk_sp<SkShader> shader = image->makeShader(SkTileMode::kRepeat);
// 自动释放,无需手动管理
// ❌ 避免:原始指针容易内存泄漏
SkImage* image = SkImage::MakeFromEncoded(data).release();
// 需要手动调用image->unref()
及时释放资源:
// ✅ 作用域结束自动释放
{
SkBitmap bitmap;
bitmap.allocN32Pixels(1024, 1024);
// 使用bitmap...
} // bitmap析构,内存释放
// ❌ 长时间持有大对象
class MyClass {
SkBitmap hugeBitmap; // 可能占用数MB内存
// 如果hugeBitmap不再使用,应及时reset()
};
2. 性能优化
复用SkPaint对象:
// ✅ 复用Paint对象
SkPaint paint;
for (int i = 0; i < 1000; i++) {
paint.setColor(colors[i]);
canvas->drawRect(rects[i], paint);
}
// ❌ 重复创建Paint对象(开销大)
for (int i = 0; i < 1000; i++) {
SkPaint paint; // 每次创建/析构
paint.setColor(colors[i]);
canvas->drawRect(rects[i], paint);
}
使用saveLayer而非创建新Surface:
// ✅ 高效:使用saveLayer
canvas->saveLayer(nullptr, &alphaPaint);
drawComplexContent(canvas);
canvas->restore();
// ❌ 低效:创建离屏Surface
sk_sp<SkSurface> offscreen = SkSurface::MakeRaster(...);
SkCanvas* offscreenCanvas = offscreen->getCanvas();
drawComplexContent(offscreenCanvas);
sk_sp<SkImage> image = offscreen->makeImageSnapshot();
canvas->drawImage(image, 0, 0, &alphaPaint);
避免过度绘制:
// ✅ 清除不可见区域
canvas->clear(SK_ColorWHITE);
canvas->clipRect(visibleRect); // 仅绘制可见区域
drawContent(canvas);
// ❌ 绘制大量不可见内容
canvas->clear(SK_ColorWHITE);
drawEntireScene(canvas); // 包括屏幕外内容
3. 路径优化
简化路径:
// 复杂路径简化
SkPath complexPath = ...;
SkPath simplifiedPath;
Simplify(complexPath, &simplifiedPath); // PathOps简化
canvas->drawPath(simplifiedPath, paint); // 绘制简化路径
路径变换缓存:
// ✅ 缓存变换后的路径
class PathCache {
SkPath originalPath;
SkMatrix lastMatrix;
SkPath transformedPath;
const SkPath& getTransformed(const SkMatrix& matrix) {
if (matrix != lastMatrix) {
originalPath.transform(matrix, &transformedPath);
lastMatrix = matrix;
}
return transformedPath;
}
};
// ❌ 每次都变换
for (int i = 0; i < 100; i++) {
SkPath transformed;
originalPath.transform(matrix, &transformed);
canvas->drawPath(transformed, paint);
}
4. 图像处理
选择合适的颜色类型:
// 不透明图像使用RGB_888(节省25%内存)
SkImageInfo info = SkImageInfo::Make(
width, height,
kRGB_888x_SkColorType, // 无Alpha通道
kOpaque_SkAlphaType
);
// 需要透明度才使用RGBA_8888
SkImageInfo infoWithAlpha = SkImageInfo::Make(
width, height,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType
);
图像缩放优化:
// ✅ 使用适当的采样选项
SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kLinear);
canvas->drawImageRect(image, src, dst, sampling, &paint);
// 或针对不同场景:
// 缩小图像:使用Mipmap
SkSamplingOptions downsample(SkFilterMode::kLinear, SkMipmapMode::kLinear);
// 放大图像:双三次插值
SkSamplingOptions upsample(SkCubicResampler::Mitchell());
// 像素艺术:最近邻
SkSamplingOptions pixelArt(SkFilterMode::kNearest);
5. 文本渲染
使用TextBlob缓存:
// ✅ 缓存TextBlob(文本不变时复用)
class TextRenderer {
sk_sp<SkTextBlob> cachedBlob;
SkString lastText;
SkFont lastFont;
void drawText(SkCanvas* canvas, const char* text, const SkFont& font) {
if (text != lastText || font != lastFont) {
SkTextBlobBuilder builder;
// 构建TextBlob...
cachedBlob = builder.make();
lastText = text;
lastFont = font;
}
canvas->drawTextBlob(cachedBlob, x, y, paint);
}
};
// ❌ 每次都构建TextBlob
canvas->drawString(text, x, y, font, paint); // 每次都整形
6. GPU优化
批量提交绘制命令:
// ✅ 批量绘制
canvas->save();
for (int i = 0; i < 1000; i++) {
canvas->drawRect(rects[i], paint);
}
canvas->restore();
context->flush(); // 一次提交所有命令
// ❌ 频繁flush
for (int i = 0; i < 1000; i++) {
canvas->drawRect(rects[i], paint);
context->flush(); // 1000次GPU提交!
}
控制资源缓存大小:
// 设置合理的GPU缓存限制
GrDirectContext* context = ...;
context->setResourceCacheLimit(100 * 1024 * 1024); // 100MB
// 低内存设备
context->setResourceCacheLimit(32 * 1024 * 1024); // 32MB
// 内存压力时清理缓存
context->freeGpuResources();
7. 调试技巧
启用调试信息:
// 编译选项
-DSK_DEBUG
-DSK_DEVELOPER
// 运行时日志
SkDebugf("Drawing rect: %f,%f,%f,%f\n", rect.left(), rect.top(),
rect.right(), rect.bottom());
// GPU调试
GrDirectContext* context = ...;
GrBackendApi backend = context->backend();
SkDebugf("GPU Backend: %s\n", GrBackendApiToStr(backend));
性能分析:
// Android Trace
#include <utils/Trace.h>
ATRACE_CALL(); // 自动追踪函数
{
ATRACE_NAME("Skia::DrawComplexPath");
canvas->drawPath(complexPath, paint);
}
// Skia内置性能追踪
SkAutoTrace trace("DrawScene");
drawScene(canvas);
总结
Skia核心价值
- 跨平台一致性: 在不同平台上提供像素级一致的渲染结果
- 高性能: GPU加速 + CPU优化,满足60fps+ 流畅体验
- 完整功能: 从基础绘图到高级特效,覆盖所有2D图形需求
- 生产就绪: 经过Chrome、Android、Flutter数十亿设备验证
- 持续演进: 活跃开发(Ganesh → Graphite架构升级)
技术亮点
架构设计:
- ✅ 清晰的分层架构:API → Core → Backend
- ✅ 双渲染管线:CPU软件 + GPU硬件
- ✅ 模块化设计:独立的编解码、文本、特效系统
- ✅ 平台抽象:统一API,多后端实现
性能优化:
- ✅ GPU加速:Ganesh后端,批量渲染
- ✅ 智能缓存:纹理图集、字形缓存、着色器缓存
- ✅ SIMD优化:SSE、AVX、NEON指令集
- ✅ 延迟渲染:Picture录制、DDL异步提交
功能丰富:
- ✅ 11+ 图像格式支持
- ✅ 29种混合模式
- ✅ 30+ 图像滤镜
- ✅ 复杂路径运算(Boolean ops)
- ✅ 高级文本整形(HarfBuzz)
- ✅ 色彩空间管理
- ✅ PDF/SVG渲染
适用场景
✅ 适用:
- 移动应用UI渲染(Android)
- 浏览器渲染引擎(Chrome)
- 跨平台UI框架(Flutter)
- 游戏2D渲染
- 图形编辑器
- 数据可视化
- 文档渲染(PDF)
❌ 不适用:
- 3D图形渲染(使用OpenGL/Vulkan/Metal直接)
- 实时视频处理(使用专用解码器)
- 科学计算(使用CUDA/OpenCL)
性能数据
| 操作 | 软件渲染 | GPU渲染 | 加速比 |
|---|---|---|---|
| 简单矩形 | 1000k/s | 10000k/s | 10x |
| 复杂路径 | 100k/s | 5000k/s | 50x |
| 文本渲染 | 50k字符/s | 500k字符/s | 10x |
| 图像缩放 | 200 MB/s | 2000 MB/s | 10x |
| 模糊滤镜 | 50 MB/s | 1000 MB/s | 20x |
最终建议
应用开发:
- 优先使用GPU渲染(SkSurface::MakeFromBackendRenderTarget)
- 复用SkPaint、SkPath等对象
- 缓存TextBlob、变换后的路径
- 批量提交绘制命令,减少flush
- 选择合适的颜色类型和采样选项
性能优化:
- 使用SIMD优化的软件渲染(NEON/SSE)
- 启用GPU缓存,设置合理限制
- 避免过度绘制,使用clipRect
- 延迟渲染(SkPicture、DDL)
- 监控内存使用,及时释放资源
调试分析:
- 使用Android Trace分析性能
- 启用Skia调试日志
- 检查GPU缓存命中率
- 分析绘制调用次数
- 使用金标准测试验证渲染结果
参考资源
官方文档
源码
学习资源
- Skia Graphics Library - Google I/O演讲
- How Skia Works - 架构解析
- Flutter渲染原理 - Skia应用案例
工具
“Skia: 为全球数十亿设备提供流畅、一致的2D图形体验”
更多推荐



所有评论(0)