Picture 录制与回放
Picture 是 tgfx 提供的绘图命令录制与回放机制。通过 Picture,你可以将一系列绘图命令录制下来,之后在任意 Canvas 上多次回放,实现"录制一次、多次使用"的高效渲染模式。
1. 概述
1.1 什么是 Picture
Picture 是一个不可变的绘图命令集合。它捕获了在 Canvas 上执行的所有绘图操作(如 drawRect、drawPath、drawImage 等),并可以在之后的任意时刻回放这些命令。
1.2 核心价值
| 特性 | 说明 |
|---|---|
| 命令复用 | 复杂的绘图序列只需录制一次,可多次回放 |
| 线程安全 | Picture 一旦创建即不可变,可安全地在多线程间共享 |
| 延迟渲染 | 录制时不执行实际渲染,回放时才真正绑定 |
| 转换灵活 | 可转换为 Image 用于后续合成或缓存 |
1.3 典型应用场景
- 重复元素绘制:如列表项、图标、徽章等需要多次绘制的内容
- 复杂图形缓存:将计算开销大的绘制序列缓存为 Picture
- 跨 Surface 复用:同一 Picture 可以在不同 Surface 的 Canvas 上回放
2. 录制流程
Picture 的录制遵循 PictureRecorder → Canvas → Picture 的流程:

2.1 基本录制步骤
int main() {
// 1. 创建 PictureRecorder
PictureRecorder recorder;
// 2. 开始录制,获取录制用的 Canvas
Canvas* canvas = recorder.beginRecording();
// 3. 在 Canvas 上执行绑定命令(这些命令会被录制)
Paint paint;
paint.setColor(Color::Blue());
canvas->drawRect(Rect::MakeXYWH(0, 0, 100, 100), paint);
canvas->drawCircle(50, 50, 30, paint);
// 4. 结束录制,获取 Picture
auto picture = recorder.finishRecordingAsPicture();
// picture 现在包含了上述所有绘图命令
return 0;
}
2.2 API 说明
| 方法 | 说明 |
|---|---|
beginRecording() | 开始录制,返回用于录制的 Canvas。如果已在录制中,会清空现有命令重新开始 |
getRecordingCanvas() | 获取当前正在录制的 Canvas,如果未在录制中返回 nullptr |
finishRecordingAsPicture() | 结束录制并返回 Picture。如果未录制任何命令,返回 nullptr |
2.3 注意事项
- 录制 Canvas 由 PictureRecorder 管理,不要手动删除
- 录制过程中可以使用 Canvas 的所有绑定 API,包括
save()/restore()、变换、裁剪等 - 录制的命令不会立即执行,只有在回放时才会真正渲染
3. Picture 回放
Canvas 提供两种 drawPicture 重载来回放 Picture:

3.1 简单回放
直接将 Picture 的内容绘制到当前 Canvas,使用 Canvas 当前的裁剪区域和变换矩阵:
void drawPicture(std::shared_ptr<Picture> picture);
示例:
int main() {
// 假设 picture 已通过 PictureRecorder 录制
auto picture = createPicture();
// 获取目标 Canvas
auto canvas = surface->getCanvas();
// 直接回放 Picture
canvas->drawPicture(picture);
return 0;
}
3.2 带变换和属性的回放
支持在回放时应用额外的变换矩阵和 Paint 属性:
void drawPicture(std::shared_ptr<Picture> picture,
const Matrix* matrix,
const Paint* paint);
| 参数 | 说明 |
|---|---|
matrix | 额外的变换矩阵,与 Canvas 当前矩阵组合使用。传 nullptr 表示不额外变换 |
paint | Paint 属性(如透明度、滤镜等)。传 nullptr 表示不应用额外属性 |
重要:如果提供了 paint 参数,Picture 会先绘制到一个临时图层,再应用 Paint 属性后渲染到目标 Canvas。
示例:
int main() {
auto picture = createPicture();
auto canvas = surface->getCanvas();
// 带缩放变换回放
Matrix scale = Matrix::MakeScale(2.0f, 2.0f);
canvas->drawPicture(picture, &scale, nullptr);
// 带半透明效果回放
Paint paint;
paint.setAlpha(0.5f);
canvas->drawPicture(picture, nullptr, &paint);
// 同时应用变换和透明度
Matrix translate = Matrix::MakeTrans(100, 100);
canvas->drawPicture(picture, &translate, &paint);
return 0;
}
3.3 回放 vs playback
Picture 还有一个 playback() 方法,它与 drawPicture() 的区别是:
| 方法 | 行为 |
|---|---|
canvas->drawPicture(picture) | 作为单个绘制命令添加到 Canvas,Canvas 的状态不受 Picture 内容影响 |
picture->playback(canvas) | 将 Picture 中的命令逐个发送到 Canvas,如同直接在 Canvas 上执行 |
一般情况下推荐使用 drawPicture(),因为它会自动保护 Canvas 的状态。
4. 缓存与性能优化
4.1 录制一次、多次回放
Picture 的核心价值在于将绘图命令的构建与执行分离。对于需要重复绘制的内容,只需录制一次:
int main() {
// 录制一个复杂的图标
auto iconPicture = recordIcon();
auto canvas = surface->getCanvas();
// 在多个位置绘制同一个图标
for (int i = 0; i < 100; i++) {
canvas->save();
canvas->translate(i * 50.0f, 0);
canvas->drawPicture(iconPicture);
canvas->restore();
}
return 0;
}
std::shared_ptr<Picture> recordIcon() {
PictureRecorder recorder;
auto canvas = recorder.beginRecording();
// 绘制复杂图标(假设有很多绘制调用)
Paint paint;
paint.setColor(Color::Red());
canvas->drawCircle(20, 20, 18, paint);
paint.setColor(Color::White());
canvas->drawRect(Rect::MakeXYWH(15, 10, 10, 20), paint);
return recorder.finishRecordingAsPicture();
}
4.2 适用场景分析
| 场景 | 是否推荐使用 Picture |
|---|---|
| 重复绘制相同内容 | ✅ 强烈推荐 |
| 复杂路径/形状组合 | ✅ 推荐 |
| 动态变化的内容 | ❌ 不推荐(每帧都需重新录制) |
| 简单的单次绘制 | ❌ 不推荐(增加额外开销) |
4.3 内存与性能权衡
- 内存:Picture 会持有录制时引用的资源(如 Image、Path 等)
- 线程安全:Picture 创建后不可变,可安全跨线程使用
- GPU 资源:Picture 本身不占用 GPU 资源,回放时才会使用
5. Picture 转 Image
通过 Image::MakeFrom() 可以将 Picture 转换为 Image,适用于需要将绘制结果作为纹理使用的场景:
static std::shared_ptr<Image> MakeFrom(
std::shared_ptr<Picture> picture,
int width,
int height,
const Matrix* matrix = nullptr,
std::shared_ptr<ColorSpace> colorSpace = nullptr);
5.1 基本用法
int main() {
// 录制 Picture
auto picture = recordComplexGraphics();
// 将 Picture 转换为 200x200 的 Image
auto image = Image::MakeFrom(picture, 200, 200);
// 现在可以像使用普通 Image 一样使用它
auto canvas = surface->getCanvas();
canvas->drawImage(image, 0, 0);
return 0;
}
5.2 带变换的转换
可以在转换时应用变换矩阵,调整 Picture 在 Image 中的位置和大小:
int main() {
auto picture = recordComplexGraphics();
// 将 Picture 缩放 0.5 倍后转为 Image
Matrix scale = Matrix::MakeScale(0.5f, 0.5f);
auto image = Image::MakeFrom(picture, 100, 100, &scale);
return 0;
}
5.3 应用场景
- 纹理缓存:将复杂绘制转为 Image 后作为纹理重复使用
- 离屏渲染:先渲染到 Picture,再转为 Image 进行后续合成
- 截图功能:捕获绘制内容并导出
6. 高级用法
6.1 AbortCallback 中断回放
Picture 支持通过回调在回放过程中中断:
class MyAbortCallback : public Picture::AbortCallback {
public:
bool abort() override {
// 返回 true 会立即中断回放
return shouldStop;
}
bool shouldStop = false;
};
int main() {
auto picture = recordPicture();
auto canvas = surface->getCanvas();
MyAbortCallback callback;
// 可以在另一个线程中设置 callback.shouldStop = true 来中断
picture->playback(canvas, &callback);
return 0;
}
6.2 获取边界信息
int main() {
auto picture = recordPicture();
// 获取 Picture 的边界框
Rect bounds = picture->getBounds();
// 检查是否包含无界填充(如反向路径填充)
bool hasUnbounded = picture->hasUnboundedFill();
return 0;
}
注意:getBounds() 返回的边界仅包含绘制命令的几何范围。如果 hasUnboundedFill() 返回 true,表示实际绘制可能超出此边界。
