TGFX - 腾讯开源的轻量级 2D 渲染引擎

TGFX - 腾讯开源的轻量级 2D 渲染引擎

  • 首页
  • 下载
  • 文档
  • 案例
  • CN
  • GitHub
  • 论坛交流
  • Languages iconCN
    • EN

›图像与像素

快速开始

  • TGFX 简介
  • 平台与后端支持
  • 环境准备与编译
  • Hello2D 示例

API 参考与概述

    绘图基础

    • Canvas Overview
    • Paint Overview
    • Path Overview
    • BlendMode Overview
    • Picture 录制与回放

    几何与变换

    • 几何与变换

    图像与像素

    • Image
    • Bitmap 与像素操作
    • 图像编解码
    • 视频与外部纹理

    文本渲染

    • 文本与字体

    着色与效果

    • 着色与效果

    图层系统

    • 图层系统

    进阶主题

    • 自定义 Shader
    • 色彩管理

架构设计

  • 渲染管线
  • GPU 硬件抽象层
  • 图层渲染系统
  • 缓存系统
  • 文字图集渲染
  • GPU Hairline 极细描边
  • 广色域渲染
  • SIMD 加速

API 文档

  • API 文档

视频与外部纹理

在处理 60fps 的高频内容更新(如视频或直播)时,ImageReader 提供了连接视频源与渲染管线的高速通路。

视频内容的流动性要求显存能够极其高效地重用。

  • 零拷贝策略:视频数据直接从硬件解码器进入 GPU 显存,跳过了 CPU 内存的搬运。
  • 同步驱动:通过 Poll 模型确保每一帧内容与渲染循环保持一致。

1. ImageReader 核心流程

ImageReader 通过内部的 ImageStream 连接平台原生视频源,驱动从视频流到 GPU 纹理的高速流转:

原生视频源 (Camera/Player) → ImageStream → ImageReader (帧管理) → acquireNextBuffer (ImageBuffer) → Image::MakeFrom (包装) → Canvas (绘制)

1.1 使用步骤

  1. 获取帧:在渲染循环中调用 reader->acquireNextBuffer()。该操作返回 std::shared_ptr<ImageBuffer>,未就绪时返回 nullptr。
  2. 包装视图:通过 Image::MakeFrom(buffer) 创建不可变图像。
  3. 渲染:GPU 在内部自动完成 YUV 到 RGB 的色彩空间转换。

1.2 完整代码示例

// Android example: Create reader from SurfaceTexture
auto reader = SurfaceTextureReader::Make(videoWidth, videoHeight, frameListener);
auto inputSurface = reader->getInputSurface();
// ... configure video decoder to output to inputSurface ...

// Render loop
void onDrawFrame(Canvas* canvas) {
    auto buffer = reader->acquireNextBuffer();
    if (buffer == nullptr) {
        return;  // No new frame available
    }
    auto image = Image::MakeFrom(buffer);
    canvas->drawImage(image, 0, 0);
}

1.3 平台视频源

平台子类视频源创建方式
AndroidSurfaceTextureReaderSurfaceTextureMake(width, height, listener)
WebVideoElementReaderHTMLVideoElementMakeFrom(video, width, height)
iOS / macOS通过 HardwareBufferCVPixelBuffer使用 Image::MakeFrom(HardwareBuffer)

Android 特殊说明:SurfaceTextureReader 需要传入一个实现了 SurfaceTexture.OnFrameAvailableListener 的 Java 对象作为 listener。当视频帧可用时,listener 的回调需要通过 JNI 调用 notifyFrameAvailable() 通知 Reader,否则此前获取的 ImageBuffer 将无法生成纹理。


2. YUV 数据导入

除了通过 ImageReader 从视频源读取,TGFX 还支持直接从原始 YUV 数据创建图像:

  • Image::MakeI420(yuvData, colorSpace):从 I420 平面数据创建图像。
  • Image::MakeNV12(yuvData, colorSpace):从 NV12 交织数据创建图像。

2.1 YUVData 创建

YUVData 是一个不可变的多平面像素容器,通过 MakeFrom 工厂方法创建:

static std::shared_ptr<YUVData> MakeFrom(
    int width, int height,
    const void** data,        // Array of base addresses for each plane
    const size_t* rowBytes,   // Array of bytes-per-row for each plane
    size_t planeCount,        // I420: 3, NV12: 2
    ReleaseProc releaseProc = nullptr,  // Called when YUVData is destroyed
    void* context = nullptr);           // User data for release callback

注意:如果 releaseProc 为 nullptr,调用者必须确保传入的数据指针在 YUVData 的整个生命周期内保持有效。

代码示例:从 I420 数据创建 Image

// Assume we have I420 data from a video decoder
const void* planes[3] = {yPlane, uPlane, vPlane};
size_t rowBytes[3] = {yStride, uStride, vStride};

auto yuvData = YUVData::MakeFrom(width, height, planes, rowBytes,
                                  YUVData::I420_PLANE_COUNT,
                                  releaseCallback, userData);
auto image = Image::MakeI420(yuvData, YUVColorSpace::BT709_LIMITED);
canvas->drawImage(image, 0, 0);

2.2 常见布局

  • I420:平面模式,Y/U/V 三个通道分别存储(共 3 个 plane)。
  • NV12:交织模式(Y 平面 + UV 交织,共 2 个 plane),移动端系统的默认采集格式。

2.3 YUVColorSpace (转换矩阵)

每种标准都区分 Limited 和 Full 范围:

  • BT601_LIMITED / BT601_FULL:标清视频(默认值)。
  • BT709_LIMITED / BT709_FULL:高清视频。
  • BT2020_LIMITED / BT2020_FULL:超高清视频。
  • JPEG_FULL:JPEG 专用的全范围模式。

色彩范围差异:

  • Limited 范围:Y 分量 [16-235],U/V 分量 [16-240]。这是录制视频的标准范围。
  • Full 范围:所有分量 [0-255]。JPEG 图片默认使用此范围。

色彩注意:若视频看起来发灰或颜色偏差,通常是因为 YUVColorSpace 参数与原始流信息不匹配导致。特别注意 Limited 与 Full 的区分——录制的视频通常为 Limited 范围,而 JPEG 图片为 Full 范围。


3. 安全管理:双缓冲与过期规则

显存重用与安全性的平衡。

3.1 自动过期机制 (Expiration)

  • 规则:同一个 ImageReader 生成的所有 ImageBuffer 共享同一份内部纹理。当新获取的 ImageBuffer 被绘制后,此前获取的 ImageBuffer 将自动失效。这意味着同时可访问的有效 ImageBuffer 最多为两个(当前帧 + 下一帧)。
  • 约束:严禁在类的成员变量中跨帧持久化持有 ImageBuffer。
  • 后果:访问过期帧时 ImageBuffer::expired() 返回 true,其无法再创建纹理。

代码示例:安全帧管理

void onDrawFrame(Canvas* canvas) {
    auto buffer = reader->acquireNextBuffer();
    if (buffer == nullptr || buffer->expired()) {
        return;  // No valid frame
    }
    auto image = Image::MakeFrom(buffer);
    canvas->drawImage(image, 0, 0);
    // Do NOT store 'buffer' as a member variable across frames
}

3.2 线程与上下文绑定

  • Context 绑定:由 ImageReader 产生的 ImageBuffer 在首次被绘制时会绑定到对应的 GPU Context,之后无法再在其他 Context 上使用。
  • 线程安全:ImageBuffer 的属性访问(width()、height()、expired() 等)在所有线程上都是安全的,即使 buffer 已过期。

4. 渲染效果参考

YUV I420 数据渲染

通过 YUVData 和 Image::MakeI420 将 I420 平面数据渲染为 RGB 图像:

int width = 200;
int height = 200;

// Allocate I420 planes: Y (full size), U/V (quarter size each)
std::vector<uint8_t> yPlane(width * height);
std::vector<uint8_t> uPlane(width / 2 * height / 2);
std::vector<uint8_t> vPlane(width / 2 * height / 2);

// Fill Y plane with horizontal brightness gradient
for (int row = 0; row < height; ++row) {
    for (int col = 0; col < width; ++col) {
        yPlane[row * width + col] = static_cast<uint8_t>(col * 235 / width + 16);
    }
}
// Fill U/V planes with vertical color gradient
for (int row = 0; row < height / 2; ++row) {
    for (int col = 0; col < width / 2; ++col) {
        uPlane[row * (width / 2) + col] = static_cast<uint8_t>(row * 200 / (height / 2) + 28);
        vPlane[row * (width / 2) + col] = static_cast<uint8_t>(200 - row * 150 / (height / 2));
    }
}

// Create YUVData and Image
const void* planes[3] = {yPlane.data(), uPlane.data(), vPlane.data()};
size_t rowBytes[3] = {(size_t)width, (size_t)(width / 2), (size_t)(width / 2)};
auto yuvData = YUVData::MakeFrom(width, height, planes, rowBytes, YUVData::I420_PLANE_COUNT);
auto yuvImage = Image::MakeI420(yuvData, YUVColorSpace::BT601_FULL);

// Draw: GPU automatically converts YUV to RGB
canvas->drawImage(yuvImage, 0, 0);

Video YUV Frame


5. 协作提示

同步注意:开发者需自行结合平台的帧可用回调(如 onFrameAvailable)来驱动 Canvas 重新发起绘制指令。

← 图像编解码文本与字体 →
  • 1. ImageReader 核心流程
    • 1.1 使用步骤
    • 1.2 完整代码示例
    • 1.3 平台视频源
  • 2. YUV 数据导入
    • 2.1 YUVData 创建
    • 2.2 常见布局
    • 2.3 YUVColorSpace (转换矩阵)
  • 3. 安全管理:双缓冲与过期规则
    • 3.1 自动过期机制 (Expiration)
    • 3.2 线程与上下文绑定
  • 4. 渲染效果参考
    • YUV I420 数据渲染
  • 5. 协作提示
公司地址:广东省深圳市南山区海天二路33号腾讯滨海大厦Copyright © 2018 - 2026 Tencent. All Rights Reserved.联系电话:0755-86013388隐私政策