Skia图形库:绘制2D世界的核心组件

文章摘要

Skia是Google开发的跨平台2D图形库,核心组件包括:SkCanvas(绘图入口)、SkPaint(画笔样式)、SkPath(几何形状)、SkImage/SkBitmap(图片处理)、SkFont/SkTypeface(文本渲染)以及SkSurface(画布载体)。支持CPU/GPU/PDF/SVG多种渲染后端,广泛应用于Chrome、Flutter等项目中,提供从基础绘图到高级特效的全套图形能力。调用流程为创建Surface→获取Canvas→设置Paint→执行绘制→输出结果,形成完整的图形渲染解决方案。

Skia 是 Google 开发的一个高性能、跨平台的 2D 图形库,被广泛应用于 Chrome、Android、Flutter 等项目。它的主要作用是提供底层的图形绘制能力,比如画线、画圆、渲染文本、处理图片等。

下面用通俗易懂的方式介绍 Skia 的主要组成部分:


1. Skia 的核心组成模块

1.1 SkCanvas(画布)

  • 作用:所有绘图操作的入口,类似于一张可以画画的纸。
  • 功能:你可以在 SkCanvas 上画线、画矩形、画图片、画文本等。

1.2 SkPaint(画笔)

  • 作用:描述“怎么画”,比如颜色、线宽、填充样式、抗锯齿等。
  • 功能:每次绘图时都要指定 SkPaint,告诉 Skia 用什么样的风格来画。

1.3 SkPath(路径)

  • 作用:描述复杂的几何形状,比如曲线、多边形等。
  • 功能:可以用来画贝塞尔曲线、圆弧、任意多边形等。

1.4 SkImage/SkBitmap(图片/位图)

  • 作用:用于加载、存储和显示图片。
  • 区别:SkBitmap 偏向于像素数据的存储,SkImage 更偏向于只读和跨平台。

1.5 SkTypeface/SkFont(字体/字体样式)

  • 作用:用于文本的字体管理和渲染。
  • 功能:支持多种字体格式(如 TrueType、OpenType),可以设置字号、粗细等。

1.6 SkShader/SkColorFilter/SkMaskFilter(着色器/颜色滤镜/遮罩滤镜)

  • 作用:实现渐变、图案填充、颜色变换、模糊等高级效果。

1.7 SkSurface(表面)

  • 作用:SkCanvas 的实际承载者,管理底层的像素缓冲区。
  • 功能:可以创建基于内存、GPU、PDF 等不同类型的 Surface。

1.8 SkDocument(文档)

  • 作用:用于生成 PDF、SVG 等文档格式的输出。

2. Skia 的后端(Backend)

Skia 支持多种底层渲染后端:

  • CPU 渲染:纯软件绘制,适合没有 GPU 的环境。
  • GPU 渲染:基于 OpenGL、Vulkan、Metal、Direct3D 等,适合高性能场景。
  • PDF/SVG 输出:可以直接生成 PDF 或 SVG 文件。

3. Skia 的整体架构图(简化版)

+-------------------+
|   应用层(如Flutter、Chrome) |
+-------------------+
           |
           v
+-------------------+
|      Skia API     |  <--- SkCanvas, SkPaint, SkPath, SkImage, SkFont, etc.
+-------------------+
           |
           v
+-------------------+
|   渲染后端(CPU/GPU/PDF/SVG) |
+-------------------+

4. Skia 的典型调用流程

  1. 创建 SkSurface(画布承载体)
  2. 获取 SkCanvas
  3. 用 SkPaint 设置画笔样式
  4. 用 SkCanvas 画图形、图片、文本等
  5. 输出到屏幕、图片、PDF 等

5. 总结口诀

  • SkCanvas:我要画
  • SkPaint:怎么画
  • SkPath:画什么形状
  • SkImage/SkBitmap:画图片
  • SkFont/SkTypeface:画文字
  • SkSurface:画在哪里
  • 后端:用什么方式画

下面我会分三部分继续详细介绍:


1. Skia 的源码结构

Skia 的源码结构非常清晰,主要目录和功能如下(以 Skia GitHub 主干为例):

目录/文件 主要内容说明
include/ Skia 对外公开的 C++ 头文件(API 入口)
src/ Skia 的核心实现代码(大部分功能都在这里)
src/core/ 2D 图形核心算法(如 SkCanvas、SkPaint、SkPath、SkImage 等的实现)
src/gpu/ GPU 渲染相关代码(OpenGL、Vulkan、Metal、D3D 后端)
src/sksl/ Skia Shading Language(SkSL)着色器语言相关
src/pdf/ PDF 输出相关实现
src/svg/ SVG 解析与输出相关实现
src/utils/ 各种工具类、辅助功能
src/text/ 文字排版与渲染相关实现
modules/ 可选模块(如 SkParagraph、Skottie、SkShaper 等)
tools/ Skia 的测试工具、示例程序
gm/ 图形测试用例
platform_tools/ 平台相关的工具(如 Android、iOS、Windows、Linux 的适配代码)
third_party/ 依赖的第三方库(如 HarfBuzz、libpng、zlib 等)
experimental/ 实验性功能
tests/ 单元测试代码

常用入口文件:

  • include/core/SkCanvas.h
  • include/core/SkPaint.h
  • include/core/SkPath.h
  • include/core/SkImage.h
  • include/core/SkFont.h
  • include/core/SkSurface.h

2. Skia 各模块的详细 API 简介

2.1 SkCanvas

  • 作用:所有绘图操作的核心入口。
  • 常用方法
    • drawRectdrawCircledrawPathdrawImagedrawTextBlobdrawLine 等。
    • save()/restore():保存/恢复画布状态(如变换、裁剪等)。
    • translate()scale()rotate():变换画布。

2.2 SkPaint

  • 作用:描述绘图风格。
  • 常用属性/方法
    • setColor():设置颜色
    • setStrokeWidth():线宽
    • setStyle():填充/描边
    • setAntiAlias():抗锯齿
    • setShader():渐变、图案等

2.3 SkPath

  • 作用:描述复杂路径。
  • 常用方法
    • moveTo()lineTo()quadTo()cubicTo()arcTo()close()
    • addRect()addCircle()addOval()addPath()

2.4 SkImage / SkBitmap

  • 作用:图片的载体。
  • 常用方法
    • MakeFromEncoded():从字节流解码图片
    • readPixels()peekPixels():访问像素数据

2.5 SkFont / SkTypeface

  • 作用:字体管理与文本绘制。
  • 常用方法
    • SkFont::setSize():字号
    • SkFont::setTypeface():字体
    • SkCanvas::drawString()drawTextBlob():绘制文本

2.6 SkSurface

  • 作用:画布的实际承载体。
  • 常用方法
    • MakeRaster():创建内存画布
    • MakeRenderTarget():创建 GPU 画布
    • getCanvas():获取 SkCanvas

2.7 SkShader / SkColorFilter / SkMaskFilter

  • 作用:实现渐变、滤镜、模糊等效果。
  • 常用方法
    • SkShader::MakeLinearGradient()MakeRadialGradient()
    • SkColorFilter::MakeLightingFilter()
    • SkMaskFilter::MakeBlur()

2.8 SkDocument

  • 作用:生成 PDF、SVG 等文档。
  • 常用方法
    • SkDocument::MakePDF()MakeXPS()MakeSVG()

3. Skia 在 Flutter/Chrome 中的用法

3.1 在 Flutter 中

  • Flutter 的渲染引擎(engine)底层就是用 Skia 实现的。
  • Dart 代码通过 C++ FFI 调用 Skia 的 API。
  • 典型流程:
    1. Flutter 框架生成 Widget 树。
    2. 框架层转为 Layer Tree。
    3. Engine 层将 Layer Tree 转为 Skia 的绘图命令(SkCanvas 操作)。
    4. Skia 负责最终的像素渲染(CPU 或 GPU)。

自定义绘制(CustomPainter)时,Flutter 的 Canvas API 其实就是 Skia 的 Canvas 的 Dart 封装。

3.2 在 Chrome 中

  • Chrome 的 Blink 渲染引擎,底层的 2D 绘图全部交给 Skia。
  • HTML/CSS/Canvas/图片/文本等,最终都转为 Skia 的绘图命令。
  • Skia 负责和操作系统的窗口、GPU 进行交互,输出最终的像素。

Chrome 的 PDF 导出、SVG 渲染等也用 Skia 的相关模块。


4. 参考资料

  • Skia 官方文档
  • Skia API 参考
  • Flutter Engine 源码
  • Chrome 源码(src/third_party/skia)

5. 总结

  • Skia 源码结构:核心在 src/,API 在 include/,后端和模块分明。
  • API 体系:SkCanvas、SkPaint、SkPath、SkImage、SkFont、SkSurface 等是核心。
  • 在 Flutter/Chrome 中:Skia 作为底层 2D 渲染引擎,负责所有像素级绘制。

下面我分别给出 Skia 的 C++ 原生绘图代码示例,以及 Flutter(Dart)中自定义绘制的代码示例,并简单说明 Chrome/Blink 的调用关系。


1. Skia C++ 原生绘图代码示例

假设你已经集成好 Skia,可以这样用 C++ 代码画一个红色圆和一段文字:

#include "include/core/SkCanvas.h"
#include "include/core/SkSurface.h"
#include "include/core/SkPaint.h"
#include "include/core/SkFont.h"
#include "include/core/SkImageEncoder.h"
#include "include/core/SkStream.h"

int main() {
    // 创建一个 400x400 的内存画布
    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(400, 400);
    SkCanvas* canvas = surface->getCanvas();

    // 填充白色背景
    SkPaint paint;
    paint.setColor(SK_ColorWHITE);
    canvas->drawPaint(paint);

    // 画一个红色圆
    paint.setColor(SK_ColorRED);
    paint.setAntiAlias(true);
    canvas->drawCircle(200, 200, 100, paint);

    // 画一段文字
    SkFont font;
    font.setSize(32);
    paint.setColor(SK_ColorBLACK);
    canvas->drawString("Hello Skia!", 100, 350, font, paint);

    // 保存为 PNG 文件
    sk_sp<SkImage> img = surface->makeImageSnapshot();
    SkFILEWStream out("output.png");
    img->encodeToStream(&out, SkEncodedImageFormat::kPNG, 100);

    return 0;
}

编译说明:需要链接 Skia 的库,具体编译方式请参考 Skia 官方文档。
效果:输出一个包含红色圆和文字的 PNG 图片。


2. Flutter(Dart)自定义绘制代码示例

Flutter 的 Canvas API 底层就是 Skia。下面是一个自定义画圆和文字的例子:

import 'package:flutter/material.dart';

class MyPainter extends CustomPainter {
  
  void paint(Canvas canvas, Size size) {
    // 画红色圆
    var paint = Paint()
      ..color = Colors.red
      ..isAntiAlias = true;
    canvas.drawCircle(Offset(size.width/2, size.height/2), 100, paint);

    // 画文字
    var textPainter = TextPainter(
      text: TextSpan(
        text: 'Hello Skia!',
        style: TextStyle(color: Colors.black, fontSize: 32),
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(canvas, Offset(100, 350));
  }

  
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class MySkiaDemo extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomPaint(
        size: Size(400, 400),
        painter: MyPainter(),
      ),
    );
  }
}

效果:在 Flutter 界面上显示一个红色圆和一段文字。


3. Chrome/Blink 中的 Skia 调用关系

在 Chrome/Blink 里,Web 页面上的 Canvas、SVG、图片、文本等,最终都会转成 Skia 的绘图命令。
你可以在 Chromium 源码的 third_party/skia 目录下找到 Skia 的集成代码。

例如,HTML5 Canvas 的 fillRect 最终会调用 SkCanvas::drawRect,fillText 会调用 SkCanvas::drawTextBlob 等。


4. Skia 绘制渐变、路径等进阶示例(C++)

// 画一个线性渐变矩形
SkPoint pts[2] = { {0, 0}, {400, 0} };
SkColor colors[2] = { SK_ColorBLUE, SK_ColorGREEN };
sk_sp<SkShader> shader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);

SkPaint paint;
paint.setShader(shader);
canvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 100), paint);

// 画一个路径
SkPath path;
path.moveTo(50, 300);
path.cubicTo(150, 200, 250, 400, 350, 300);
paint.setShader(nullptr);
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(4);
canvas->drawPath(path, paint);

5. Skia 官方 Demo 和 Playground

  • Skia Playground:在线试验 Skia 代码。
  • Skia Example Gallery:官方示例代码。

你可能感兴趣的:(Skia图形库:绘制2D世界的核心组件)