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 文档

Canvas Overview

Canvas 是 tgfx 绘图流水线的"第一入口",提供了丰富的 2D 绘图 API。本文档介绍 Canvas 的完整使用方法,帮助开发者快速上手 tgfx 的绑定 GPU 上下文、使用 Canvas 进行 2D 绘图,以及提交渲染结果。

1. 绘图流水线入口

1.1 创建链路概览

tgfx 的绘图流水线遵循 Device → Context → Surface → Canvas 的创建链路:

绘图流水线创建链路概览

组件职责
Device代表 GPU 设备,管理 GPU 资源的生命周期
Context渲染上下文,负责创建和管理 GPU 资源,发出绘图命令
Surface渲染目标,管理 Canvas 绑定的像素存储
Canvas绘图接口,提供所有 2D 绘图 API

1.2 代码示例

int main() {
    // 1. 从 Device 锁定 Context(线程安全)
    auto context = device->lockContext();
    
    // 2. 创建 Surface(选择下面三种方式之一)
    auto surface = Surface::Make(context, 800, 600);
    
    // 3. 获取 Canvas(由 Surface 管理生命周期)
    auto canvas = surface->getCanvas();
    
    // 4. 绑定完成,开始绑定...
    canvas->clear(Color::White());
    canvas->drawRect(Rect::MakeXYWH(100, 100, 200, 150), paint);
    
    // 5. 提交渲染
    context->flushAndSubmit();
    
    // 6. 解锁 Context
    device->unlock();
}

1.3 Surface 的三种创建方式

方式一:指定尺寸创建

在 GPU 上分配指定尺寸的渲染目标,最常用的方式。

// 创建 800x600 的 Surface,支持 MSAA(4x 采样)
auto surface = Surface::Make(context, 800, 600, 
                             false,        // alphaOnly
                             4,            // sampleCount (MSAA)
                             false,        // mipmapped
                             0,            // renderFlags
                             nullptr);     // colorSpace

方式二:从 BackendTexture 创建

包装已有的 GPU 纹理,适用于与外部纹理交互的场景。

// 从 OpenGL 纹理创建
GLTextureInfo glInfo = {textureID, GL_TEXTURE_2D, GL_RGBA8};
BackendTexture backendTexture(glInfo, width, height);
auto surface = Surface::MakeFrom(context, backendTexture, 
                                  ImageOrigin::TopLeft,
                                  1);  // sampleCount

// 从 Metal 纹理创建
MetalTextureInfo metalInfo = {metalTexture};
BackendTexture backendTexture(metalInfo, width, height);
auto surface = Surface::MakeFrom(context, backendTexture, ImageOrigin::TopLeft);

方式三:从 BackendRenderTarget 创建

包装已有的渲染目标(如 FBO),适用于渲染到外部帧缓冲的场景。

// 从 OpenGL FBO 创建
GLFrameBufferInfo glInfo = {fboID, GL_RGBA8};
BackendRenderTarget renderTarget(glInfo, width, height);
auto surface = Surface::MakeFrom(context, renderTarget, ImageOrigin::TopLeft);

// 从 Metal 纹理创建(作为渲染目标)
MetalTextureInfo metalInfo = {metalTexture};
BackendRenderTarget renderTarget(metalInfo, width, height);
auto surface = Surface::MakeFrom(context, renderTarget, ImageOrigin::TopLeft);

注意:使用 MakeFrom 创建时,调用者需确保 backend 对象在 Surface 生命周期内保持有效。


2. 状态栈

Canvas 维护一个状态栈,用于保存和恢复矩阵(matrix)与裁剪区域(clip)的状态。

2.1 save / restore

save() 保存当前状态,restore() 恢复到上一次保存的状态。

int main() {
    canvas->save();                      // 保存状态,返回 saveCount = 0
    canvas->translate(100, 100);
    canvas->drawRect(rect, paint);       // 在 (100, 100) 位置绘制
    canvas->restore();                   // 恢复状态,translate 被撤销
    
    canvas->drawRect(rect, paint);       // 在原点位置绘制
}

2.2 getSaveCount / restoreToCount

getSaveCount() 返回当前保存的层数,restoreToCount(n) 恢复到指定层。

int main() {
    int count0 = canvas->save();         // count0 = 0
    canvas->translate(50, 50);
    
    int count1 = canvas->save();         // count1 = 1
    canvas->rotate(45);
    
    int count2 = canvas->save();         // count2 = 2
    canvas->scale(2, 2);
    
    canvas->restoreToCount(count1);      // 一次性恢复到 count1,撤销 rotate 和 scale
}

2.3 saveLayer / saveLayerAlpha

saveLayer() 和 saveLayerAlpha() 在保存状态的同时,创建一个离屏图层用于后续绘制。当 restore() 时,离屏图层会应用指定的 Paint(透明度、滤镜、混合模式等)后合成到主画布。

int main() {
    // 使用 saveLayerAlpha 实现半透明绘制
    canvas->saveLayerAlpha(0.5f);        // 创建离屏图层,alpha = 0.5
    canvas->drawCircle(100, 100, 50, redPaint);
    canvas->drawCircle(130, 100, 50, bluePaint);
    canvas->restore();                   // 整体以 50% 透明度合成到主画布
    
    // 使用 saveLayer 应用 ImageFilter
    Paint layerPaint;
    layerPaint.setImageFilter(ImageFilter::Blur(10, 10));
    canvas->saveLayer(&layerPaint);
    canvas->drawImage(image, 0, 0);
    canvas->restore();                   // 应用模糊滤镜后合成
}

saveLayer 的离屏渲染本质

saveLayer 工作流程示意图

阶段操作
saveLayer()分配离屏纹理,后续绘制重定向到离屏
绑定操作所有 draw* 调用都绘制到离屏纹理
restore()离屏纹理作为 Image,应用 Paint 后绘制到主画布

性能提示:saveLayer 会分配额外的 GPU 纹理,频繁使用可能影响性能。仅在需要整体应用透明度、滤镜或特殊混合模式时使用。


3. 变换

Canvas 通过变换矩阵控制绑定内容的位置、大小、旋转等。所有变换都是左乘(pre-multiply),即"先变换,后绑定"。

3.1 变换方法一览

方法说明
translate(dx, dy)平移
scale(sx, sy)缩放
rotate(degrees)绕原点旋转
rotate(degrees, px, py)绕指定点旋转
skew(sx, sy)斜切
concat(matrix)与当前矩阵相乘
setMatrix(matrix)直接设置矩阵(覆盖)
resetMatrix()重置为单位矩阵
getMatrix()获取当前矩阵

3.2 左乘顺序的理解

Canvas 的变换是左乘(pre-multiply),这意味着代码中后写的变换先作用。

int main() {
    // 数学等价于:M = Translate * Rotate * Scale
    // 对图形的作用顺序:先 Scale → 再 Rotate → 最后 Translate
    
    canvas->translate(200, 200);   // 最后执行:移动到 (200, 200)
    canvas->rotate(45);            // 其次执行:旋转 45 度
    canvas->scale(2, 2);           // 最先执行:放大 2 倍
    
    canvas->drawRect(Rect::MakeXYWH(0, 0, 50, 50), paint);
}

左乘变换顺序示意图

3.3 concat 与 setMatrix

除了使用 translate、scale 等便捷方法外,也可以直接操作底层的变换矩阵。主要通过 concat 叠加变换,或用 setMatrix 强制替换当前变换:

  • concat(matrix):将传入的矩阵与当前矩阵进行左乘。这与调用便捷方法的行为一致,效果会累加到当前的坐标系上。
  • setMatrix(matrix):直接将当前坐标系的变换状态替换为传入的矩阵,抛弃之前的变换历史。这在需要重置或应用绝对变换时很有用。
int main() {
    // 1. concat:叠加变换矩阵
    Matrix rotation = Matrix::MakeRotate(45);
    canvas->concat(rotation);  // currentMatrix = currentMatrix * rotation
    
    // 2. setMatrix:覆盖当前矩阵
    Matrix transform = Matrix::MakeTrans(100, 100);
    canvas->setMatrix(transform);  // currentMatrix = transform
    
    // 3. resetMatrix:快速重置为单位矩阵(相当于撤销所有变换)
    canvas->resetMatrix();  // currentMatrix = I
}

4. 裁剪

Canvas 的裁剪区域(Clip)用于限制后续所有绘制操作的可见范围,只有在裁剪区域内的部分才会被真正绘制到屏幕或图像上。所有多次裁剪操作的效果都是求交集(intersect),即裁剪区域只能越裁越小。

4.1 裁剪方法

方法说明
clipRect(rect)用矩形裁剪
clipPath(path)用路径裁剪
getTotalClip()获取当前裁剪路径

4.2 代码示例

int main() {
    // 示例 1:矩形裁剪
    canvas->save(); // 保存当前无裁剪限制的干净状态
    canvas->clipRect(Rect::MakeXYWH(50, 50, 200, 200));
    canvas->drawCircle(150, 150, 100, paint);  // 圆形会被限制在矩形内
    canvas->restore(); // 恢复状态,移除矩形裁剪限制
    
    // 示例 2:路径裁剪(如椭圆裁剪)
    canvas->save(); // 再次保存无裁剪的状态
    Path clipPath;
    clipPath.addOval(Rect::MakeXYWH(100, 100, 200, 200));
    canvas->clipPath(clipPath);
    canvas->drawImage(image, 0, 0);  // 图片仅在椭圆区域内可见
    canvas->restore(); // 恢复状态
}

4.3 裁剪的求交特性

多次裁剪会取交集,裁剪区域只会越来越小。

int main() {
    canvas->clipRect(Rect::MakeXYWH(0, 0, 200, 200));    // 裁剪区域 A
    canvas->clipRect(Rect::MakeXYWH(100, 100, 200, 200)); // 裁剪区域 B
    // 最终裁剪区域 = A ∩ B = (100, 100, 100, 100)
    
    canvas->drawColor(Color::Red());  // 只有交集区域显示为红色
}

裁剪求交示意图

4.4 裁剪与变换的配合

裁剪路径会受当前矩阵影响。

int main() {
    canvas->rotate(45);
    canvas->clipRect(Rect::MakeXYWH(0, 0, 100, 100));  // 裁剪区域也旋转了 45 度
    canvas->drawImage(image, 0, 0);
}

5. 绘制方法总览

Canvas 提供了丰富的绘制方法,涵盖基础图形、路径、图片、文本等。

5.1 基础图形

方法说明
drawRect(rect, paint)绘制矩形
drawRoundRect(rect, rx, ry, paint)绘制圆角矩形
drawRRect(rrect, paint)绘制 RRect(可变圆角矩形)
drawOval(rect, paint)绘制椭圆
drawCircle(cx, cy, r, paint)绘制圆形
drawLine(x0, y0, x1, y1, paint)绘制线段
int main() {
    Paint paint;
    
    // 绘制矩形
    canvas->drawRect(Rect::MakeXYWH(50, 50, 100, 80), paint);
    
    // 绘制圆角矩形
    canvas->drawRoundRect(Rect::MakeXYWH(200, 50, 100, 80), 15, 15, paint);
    
    // 绘制圆形
    canvas->drawCircle(100, 200, 40, paint);
    
    // 绘制线段
    canvas->drawLine(200, 180, 350, 220, paint);
}

5.2 路径与形状

方法说明
drawPath(path, paint)绘制路径
drawShape(shape, paint)绘制 Shape 对象
int main() {
    // 绘制自定义路径
    Path path;
    path.moveTo(100, 50);
    path.lineTo(150, 150);
    path.lineTo(50, 150);
    path.close();  // 三角形
    
    Paint paint;
    paint.setColor(Color::Green());
    canvas->drawPath(path, paint);
    
    // 绘制 Shape(支持缓存优化)
    auto shape = Shape::MakeFrom(path);
    canvas->drawShape(shape, paint);
}

5.3 网格绘制 (Mesh)

网格绘制(Mesh)常用于实现复杂的几何体渲染或图像形变(如水波纹变形)。

在使用 drawMesh 渲染时,颜色混合规则(Color Blending Rules)非常关键。网格最终呈现的颜色取决于 Paint 的着色器(Shader)和 Mesh 是否带有顶点颜色(Vertex Colors):

  1. 有 Shader + 有顶点颜色:着色器颜色与顶点颜色相乘(Modulate 混合)。
  2. 有 Shader + 无顶点颜色:纯着色器颜色(比如纯图片纹理)。
  3. 无 Shader + 有顶点颜色:纯顶点颜色。
  4. 无 Shader + 无顶点颜色:使用 Paint 的颜色。

⚠️ 注意:只要存在 Shader 或顶点颜色,Paint 自身的颜色(paint.setColor())就不会参与最终的颜色混合计算。

方法说明
drawMesh(mesh, paint)使用当前的裁剪、变换矩阵和画笔绘制一个网格
int main() {
    // 构造 Mesh:传入顶点坐标、纹理坐标、顶点颜色、顶点索引等
    auto mesh = Mesh::Make(vertices, texCoords, colors, indices);
    
    Paint paint;
    // 根据上述规则,如果赋予了 Shader,并且 mesh 中没有顶点颜色,则完全使用 Shader 颜色
    paint.setShader(imageShader);
    
    canvas->drawMesh(mesh, paint);
}

5.4 图片绘制

方法说明
drawImage(image, paint)在坐标 (0,0) 处绘制原始尺寸图片
drawImage(image, x, y, paint)在指定坐标 (x,y) 处绘制图片
drawImage(image, sampling, paint)使用指定的采样选项(如邻近采样或线性采样)绘制图片
drawImageRect(image, dstRect, ...)将整张图片缩放绘制到目标矩形区域内
drawImageRect(image, srcRect, dstRect, ...)截取图片的部分区域(srcRect)并缩放绘制到目标矩形(dstRect)
drawAtlas(atlas, matrices[], rects[], ...)从图集(Atlas)中批量绘制多张子图,用于减少绘制调用开销(性能优化)

关于 SamplingOptions:在进行图片绘制和缩放时,可以通过 SamplingOptions 控制图像采样和过滤的质量。如果在调用 API 时未显式传入该选项,系统默认会使用线性过滤(FilterMode::Linear 和 MipmapMode::Linear)来保证图像在缩放时的平滑度。

int main() {
    auto image = Image::MakeFromFile("photo.png");
    
    // 简单绘制
    canvas->drawImage(image, 50, 50);
    
    // 绘制到指定区域(会缩放)
    canvas->drawImageRect(image, Rect::MakeXYWH(200, 50, 150, 100));
    
    // 从图片的一部分切割绘制
    Rect srcRect = Rect::MakeXYWH(0, 0, 64, 64);   // 取图片左上角 64x64
    Rect dstRect = Rect::MakeXYWH(50, 200, 128, 128);  // 放大到 128x128
    canvas->drawImageRect(image, srcRect, dstRect);
    
    // 从图集(Atlas)中批量绘制(性能优化)
    Matrix transforms[] = {Matrix::MakeTrans(0, 0), Matrix::MakeTrans(64, 0)};
    Rect rects[] = {Rect::MakeXYWH(0, 0, 32, 32), Rect::MakeXYWH(32, 0, 32, 32)};
    canvas->drawAtlas(atlas, transforms, rects, nullptr, 2);
}

5.5 文本绘制

方法说明
drawSimpleText(text, x, y, font, paint)简单文本绘制(UTF-8)
drawGlyphs(glyphs[], positions[], count, font, paint)绘制字形数组
drawTextBlob(textBlob, x, y, paint)绘制预排版的 TextBlob
int main() {
    Font font(typeface, 24);
    Paint paint;
    paint.setColor(Color::Black());
    
    // 1. 简单文本
    canvas->drawSimpleText("Hello, tgfx!", 50, 100, font, paint);
    
    // 2. 绘制字形数组(用于精确控制每个字形的渲染位置,常用于自定义排版引擎)
    GlyphID glyphs[] = { 42, 43, 44 }; // 字形索引,通常从字体中获取
    Point positions[] = { Point::Make(50, 150), Point::Make(70, 150), Point::Make(90, 150) };
    canvas->drawGlyphs(glyphs, positions, 3, font, paint);
    
    // 3. 使用 TextBlob(支持复杂排版和缓存,推荐多次绘制同一段文本时使用)
    auto textBlob = TextBlob::MakeFrom("预排版文本", font);
    canvas->drawTextBlob(textBlob, 50, 200, paint);
}

5.6 其他绘制

方法说明
clear(color)清除画布(BlendMode::Src)
drawColor(color, blendMode)填充颜色(可指定混合模式)
drawPaint(paint)用 Paint 填充整个画布
drawPicture(picture)回放 Picture 录制的命令
int main() {
    // 清除为白色
    canvas->clear(Color::White());
    
    // 半透明覆盖
    canvas->drawColor(Color::FromRGBA(0, 0, 255, 128), BlendMode::SrcOver);
    
    // 回放录制的 Picture
    canvas->drawPicture(picture);
}

6. 刷新与提交

Canvas 的绘制指令通常是异步记录的。当我们调用 drawRect、save 等 API 时,实际上只是将这些指令记录在了一个命令缓冲区(Recording)中,并没有立即交给 GPU 执行。

要让屏幕上真正显示出画面,或者让 Surface 的像素数据真正更新,必须显式地进行刷新(Flush)并提交(Submit)这些指令。

6.1 提交方式

Context 提供了灵活的提交控制,可以一步到位,也可以分步进行:

方法说明
context->flushAndSubmit(syncCpu)一步完成:直接将当前所有记录的绘图指令刷新并提交给 GPU 执行。参数 syncCpu=true 表示 CPU 会阻塞等待 GPU 执行完毕(常用于需要立即读取像素的场景)。
context->flush()仅刷新:将当前的绘图指令打包成一个 Recording 对象返回,但不提交给 GPU。此时 GPU 尚未开始工作。
context->submit(recording, syncCpu)仅提交:将之前通过 flush() 获取的 Recording 提交给 GPU 执行。

6.2 同步 vs 异步提交

int main() {
    canvas->drawRect(rect, paint);
    
    // 方式一:同步提交(等待 GPU 完成)
    context->flushAndSubmit(true);  // syncCpu = true,阻塞直到 GPU 完成
    
    // 方式二:异步提交(不等待)
    context->flushAndSubmit(false); // syncCpu = false,立即返回
}

6.3 Recording 异步提交模式

Recording 允许将绘图命令的刷新与提交分离,实现更精细的控制。

int main() {
    // 阶段 1:绑定
    canvas->drawImage(image1, 0, 0);
    canvas->drawImage(image2, 100, 0);
    
    // 阶段 2:刷新到 Recording(不发送到 GPU)
    auto recording = context->flush();
    
    // 阶段 3:可以在此做其他 CPU 工作...
    processSomeData();
    
    // 阶段 4:提交到 GPU
    if (recording) {
        context->submit(std::move(recording), false);
    }
}

注意:如果创建了多个 Recording,提交后面的 Recording 会强制先提交之前的所有 Recording,以保持正确的渲染顺序。

6.4 提交流程图

刷新与提交流程图


7. 异步像素回读

Surface::asyncReadPixels() 支持异步读取 GPU 渲染结果到 CPU 内存,避免阻塞渲染线程。

7.1 API 概览

方法说明
surface->asyncReadPixels(rect)发起异步回读,返回 SurfaceReadback
readback->isReady(context)检查数据是否就绪(非阻塞)
readback->lockPixels(context, flipY)锁定并获取像素指针(可能阻塞)
readback->unlockPixels(context)解锁像素数据

7.2 代码示例

int main() {
    // 完成绘制
    canvas->drawImage(image, 0, 0);
    context->flushAndSubmit();
    
    // 发起异步回读
    auto readback = surface->asyncReadPixels(Rect::MakeWH(800, 600));
    if (!readback) {
        return;  // 回读区域无效
    }
    
    // 方式一:轮询等待(非阻塞)
    while (!readback->isReady(context)) {
        // 做其他事情...
        std::this_thread::yield();
    }
    
    // 方式二:直接 lockPixels(会阻塞等待)
    const void* pixels = readback->lockPixels(context);
    if (pixels) {
        // 访问像素数据
        auto info = readback->info();
        // info.width(), info.height(), info.rowBytes()...
        
        // 完成后解锁
        readback->unlockPixels(context);
    }
}

7.3 flipY 参数

如果 Surface 的 origin 是 BottomLeft(OpenGL 默认),像素数据会是上下翻转的。使用 flipY = true 可以在 CPU 端进行翻转校正。

int main() {
    // 如果需要顶部为原点的像素数据
    const void* pixels = readback->lockPixels(context, true);  // flipY = true
}

8. 辅助工具

8.1 AutoCanvasRestore

RAII 风格的状态保存/恢复辅助类,构造时自动 save(),析构时自动 restore()。

int main() {
    {
        AutoCanvasRestore acr(canvas);  // 自动 save
        canvas->translate(100, 100);
        canvas->rotate(45);
        canvas->drawRect(rect, paint);
    }  // 离开作用域自动 restore
    
    // 状态已恢复
    canvas->drawRect(rect, paint);  // 在原位置绘制
}

8.2 PictureRecorder

用于录制绘图命令,生成可重复回放的 Picture 对象。适用于需要多次绘制相同内容的场景。

int main() {
    // 录制绘图命令
    PictureRecorder recorder;
    auto recordCanvas = recorder.beginRecording();
    recordCanvas->drawCircle(50, 50, 30, paint);
    recordCanvas->drawRect(rect, paint);
    auto picture = recorder.finishRecordingAsPicture();
    
    // 多次回放
    for (int i = 0; i < 5; i++) {
        canvas->save();
        canvas->translate(i * 100, 0);
        canvas->drawPicture(picture);
        canvas->restore();
    }
}

附录:快速参考

Canvas 方法速查表

分类方法
状态栈save, restore, saveLayer, saveLayerAlpha, getSaveCount, restoreToCount
变换translate, scale, rotate, skew, concat, setMatrix, resetMatrix, getMatrix
裁剪clipRect, clipPath, getTotalClip
基础图形drawRect, drawRoundRect, drawRRect, drawOval, drawCircle, drawLine
路径/形状drawPath, drawShape
图片drawImage, drawImageRect, drawAtlas
文本drawSimpleText, drawGlyphs, drawTextBlob
网格drawMesh
其他clear, drawColor, drawPaint, drawPicture

提交流程速查

// 最简方式
context->flushAndSubmit();

// 分步方式
auto recording = context->flush();
// ... do other work ...
context->submit(std::move(recording));
← Hello2D 示例Paint Overview →
  • 1. 绘图流水线入口
    • 1.1 创建链路概览
    • 1.2 代码示例
    • 1.3 Surface 的三种创建方式
  • 2. 状态栈
    • 2.1 save / restore
    • 2.2 getSaveCount / restoreToCount
    • 2.3 saveLayer / saveLayerAlpha
  • 3. 变换
    • 3.1 变换方法一览
    • 3.2 左乘顺序的理解
    • 3.3 concat 与 setMatrix
  • 4. 裁剪
    • 4.1 裁剪方法
    • 4.2 代码示例
    • 4.3 裁剪的求交特性
    • 4.4 裁剪与变换的配合
  • 5. 绘制方法总览
    • 5.1 基础图形
    • 5.2 路径与形状
    • 5.3 网格绘制 (Mesh)
    • 5.4 图片绘制
    • 5.5 文本绘制
    • 5.6 其他绘制
  • 6. 刷新与提交
    • 6.1 提交方式
    • 6.2 同步 vs 异步提交
    • 6.3 Recording 异步提交模式
    • 6.4 提交流程图
  • 7. 异步像素回读
    • 7.1 API 概览
    • 7.2 代码示例
    • 7.3 flipY 参数
  • 8. 辅助工具
    • 8.1 AutoCanvasRestore
    • 8.2 PictureRecorder
  • 附录:快速参考
    • Canvas 方法速查表
    • 提交流程速查
公司地址:广东省深圳市南山区海天二路33号腾讯滨海大厦Copyright © 2018 - 2026 Tencent. All Rights Reserved.联系电话:0755-86013388隐私政策