Cairo 图形库与 Windows 图形接口 - 代码日志

Cairo 图形库与 Windows 图形接口

https://xinlake.github.io/2017-07/cairo/

1. 简介

Cairo 是一款开源 2D 矢量图形库,支持多种输出设备,可以使用硬件加速。是 GTK+ 底层采用的图形库(GTK+ 2.8 Plan)。

这篇博客介绍将 Cairo 图形库定制移植到 Windows,用 Visual Studio 编译 Cairo DLL,以及使用 Windows 全新图形子系统 DXGI 来显示 Cairo 产生的图像。

2. Cairo 图形库定制

Cairo 是 C 语言编写的,代码模块化设计得非常好,导入或删除功能模块的代码对整体编译几乎没影响。Cairo 定义了 Surface (可以理解为画布),每种支持的输出设备都可以创建一个 Surface,比如下面两种类型的 Surface 创建函数

/**
* cairo_pdf_surface_create:
* @filename: a filename for the PDF output (must be writable), %NULL may be
* used to specify no output. This will generate a PDF surface that
* may be queried and used as a source, without generating a
* temporary file.
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a PDF surface of the specified size in points to be written
* to @filename.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
**/
cairo_surface_t *
cairo_pdf_surface_create (const char*filename,
double width_in_points,
double height_in_points);

/**
* cairo_image_surface_create:
* @format: format of pixels in the surface to create
* @width: width of the surface, in pixels
* @height: height of the surface, in pixels
*
* Creates an image surface of the specified format and
* dimensions. Initially the surface contents are all
* 0. (Specifically, within each pixel, each color or alpha channel
* belonging to format will be 0. The contents of bits within a pixel,
* but not belonging to the given format are undefined).
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.0
**/
cairo_surface_t *
cairo_image_surface_create (cairo_format_tformat,
intwidth,
intheight);

创建一个 Surface 后就可以在里面做各种绘图了。Cairo 也支持 Win32 类型的 Surface,但是这个 Surface 有些功能(如文本渲染)是调用 Windows 系统的功能来实现的。移植的目标之一是尽量减少对系统的依赖,所以移除了包括 Win32 在内的绝大部分 Surface,仅保留 Image Surface 和一些调试用模块,通过 Windows 更底层的接口来显示 Cairo 渲染好的图像。PNG 功能可以把 Image 图像压缩输出 PNG 文件。

2.1 定义 Cairo 功能

在 cairo/src 里创建文件 cairo-features.h,定义 cairo 的功能。因为会对接 Windows 的图形接口,这里只需配置 IMAGE SURFACE、FT(FreeType),此外可以按需要添加一些调试用的模块。

Cairo 功能定义

2.2 导入 Cairo 代码到 Visual Studio

创建 cairo 的 Win32 DLL 空项目,参考 src/Makefile.sources 里的代码文件列表和组织结构导入代码。创建 pixman 的 Win32 静态库空项目,参考 pixman/Makefile.sources 文件导入代码,参考 pixman 的文档可以配置硬件计算加速。cairo 工程设置依赖 pixman 工程。另外,还有 zlib、libpng、freetype 等依赖库,这些库均需要创建 Visual Studio 库工程,创建这些库工程请参阅开源库的文档。

Visual Studio 工程

2.3 编译生成 DLL

Visual Studio 编译这些库只要几分钟就可以完成,编译 cairo 会产生一千多条警告,基本上是 level 3、level 4 级别的。对于一款成熟稳健的开源库来讲,没必要修改大量的 level 3 warning。

编译 DLL

3. Windows 图形接口

从 Vista 开始,微软设计了全新的显示驱动模型,调整了图形系统架构,可以提供更好的图像质量,可以为界面提供硬件加速。参考官方文档 https://msdn.microsoft.com/en-us/library/ee417756(v=vs.85).aspx

摘自 https://msdn.microsoft.com/en-us/library/ee417756(v=vs.85).aspx

下图是 DXGI(DirectX Graphics Infrastructure),是从 Vista 开始的一个新的子系统。DXGI 用于处理一些底层任务如枚举硬件设备,创建缓存交互链(swap-chain),呈现渲染好的图像帧到输出设备等,程序可以直接访问 DXGI。参考官方文档 https://msdn.microsoft.com/en-us/library/bb205075(v=vs.85).aspx

摘自 https://msdn.microsoft.com/en-us/library/bb205075(v=vs.85).aspx

我们需要编写程序把 Cairo 的输出交给 DXGI。Cairo Image Surface 像素格式 CAIRO_FORMAT_RGB24(32 位),对应的 DXGI 像素格式是 DXGI_FORMAT_B8G8R8A8_UNORM。下面代码中 desc.OutputWindow 是关联的输出窗口。Cairo 绘图完毕后调用 cairo_image_surface_get_data 得到图像数据写入 DXGI 即显示图像

//...
unsigned char *image_data = cairo_image_surface_get_data(surface);

//...

DXGI_SWAP_CHAIN_DESC desc;
desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.OutputWindow = hWnd;
desc.Windowed = TRUE;
//...

此部分编程可以参阅读 Microsoft 官方的 Programming Guide 及 Reference,文档位于 MSDN 章节 Desktop app technologies —> Graphics and Gaming —> Direct 2D / Direct 3D / DirectX Graphics Articles

4. 结束

最初是在 WinCE 时代了解到 Cairo,当时这款图形库相比 WinCE 的 GUI 有很多优势,图像质量更好、绘图功能更多、效率更高,还可以引入 Pango 创建可以描述的 UI(像 Android 那样的通过 XML 描述创建 UI),于是便有了定制移植一整套图形库的计划。第一次构建多项目依赖的大工程耗费了不少精力(后面的迭代更新比较轻松),过程远不是本文介绍的那么简单。解决了无数大大小小的问题,直到整套的图形库软件在 WinCE、Windows XP、Windows 7 都能很好的运行。然而没等到迭代、完善,WinCE 已经没落,Android 已经慢慢流行起来了。

这套图形库现在是偶尔更新一下很少关注。放一张用移植 Cairo 在 Windows 10 下画的图,有兴趣的朋友可以在 Project Cairo 里查看更多的绘图演示。

Cairo 绘图,一组贝塞尔曲线

参考资料

cairo graphics http://cairographics.org/
GTK+ https://www.gtk.org/

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:Cairo 图形库与 Windows 图形接口