从本文开始,我将正式使用 Skia 库绘图。
本章先介绍了如何使用本系列文章的源码库(以后所有章节的源码都是按照这个方式使用的)。随后介绍了如何在一个图像文件文件中绘制一个简单的矩形。
由于是第一次使用 Skia 库绘图,所以本节介绍会详细一些,希望大家能顺利入门。
如何使用源码
本文源码存储于:文件中绘图 。
每节的代码都是一个是使用 CMake 配置的 Visual Studio 工程。
下载源码后需先修改源码文件 CMakeList.txt 第5行,第6行代码。
这两行代码用于设置 skia 的 include 路径和 library 路径。
我把 include 路径的默认值设置为:D:\sdk\skia,这是我 Skia 的源码目录。
我把 library 路径的默认值设置为:D:\sdk\skia\out,这是我 Skia 编译产物所在目录。
这个目录下有 Debug 和 Release 两个子目录,两个子目录内分别存储着 Debug 模式的 Skia 库(.lib文件)和 Release 模式的 Skia 库。
你需要把这两个路径设置成你自己的路径。
设置完成之后,使用 Visual Studio 打开本地文件夹(D:\project\SkiaInAction\绘制基本几何图形)打开你下载的本节源码目录,如下图所示(也可以在文件夹内通过右键菜单:使用Visual Studio打开):

之后就可以在 Visual Studio 中编译、运行、调试代码了,如下图所示:

简单介绍一下 Visual Studio 的操作面板
上图中①处按钮的用途为 显示所有文件,
②处为本节示例代码的源码文件:./绘制基本几何图形/src/main.cpp
③处为运行目标配置,这里选择的是x64-Debug,表示生成的是调试版本的运行程序。
④处为运行程序按钮,点击此按钮将运行程序并在⑤处生成应用程序文件(./out/build/x64-Debug/Debug/DrawInImage.exe)。
如果你把③处的运行目标配置设置成 x64-Release(生成生产版本的运行程序),再点击④处按钮后,将在⑥处生成应用程序文件(./out/build/x64-Release/Release/DrawInImage.exe),你可以把此处的应用程序文件分发给你的客户。
后续章节的示例代码都使用此方法编译、运行。
值得一提的是,本系列文章所有示例代码都没有做异常处理这类安全保证工作,比如文件路径是否存在,窗口类有没有注册成功等,之所以不做这些工作,是希望示例代码更清晰,读者更容易理解,希望大家不要直接把示例代码应用到你的产品中。
创建图片
现在来看一下 “main.cpp” 文件的代码,如下所示:
// #include <windows.h>
// #include "include/core/SkSurface.h"
// #include "include/core/SkCanvas.h"
// #include "include/encode/SkPngEncoder.h"
// #include "include/core/SkStream.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, _In_ int nCmdShow)
{
SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(800, 600);
sk_sp<SkSurface> surface = SkSurfaces::Raster(imgInfo);
SkCanvas* canvas = surface->getCanvas();
SkPaint paint;
paint.setColor(SK_ColorGREEN);
SkRect rect;
rect.setXYWH(10, 10, 100, 100);
canvas->drawRect(rect,paint);
SkPixmap pixmap;
surface->peekPixels(&pixmap);
SkFILEWStream stream("allen-image1.png");
SkPngEncoder::Encode(&stream, pixmap, {});
stream.flush();
return 0;
}
让我来逐行介绍一下这段代码:
第一次写使用 Skia 的代码,我们会介绍详细一些,后续章节我们将不再逐行介绍代码。
- 注释掉的几行代码为需要引入的头文件。在后面的章节中,一个示例可能会涉及到很多代码,分很多段落介绍。所以我尽量以注释的形式告知大家本段代码需要引入什么头文件。至于这些头文件中定义了什么类型,我们会在源码中介绍。
-
int APIENTRY wWinMain(...)这是 Windows 应用程序的入口方法,应用程序启动时,操作系统会首先调用此方法。
-
SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(800, 600);使用指定的宽度和高度创建一个
SkImageInfo对象。SkImageInfo用于描述图像的基本信息,这个对象除包含图像的宽度、高度信息外,还包含图像的颜色类型、透明度类型等信息(我会在后面的章节介绍这些信息)。 -
sk_sp<SkSurface> surface = SkSurfaces::Raster(imgInfo);根据
SkImageInfo对象创建一个SkSurface对象。此对象负责管理图像像素,可以认为它管理的就是画布上的像素。这个 surface 对象的类型是
sk_sp<SkSurface>,sk_sp<T>是 Skia 内部定义的一个智能指针类型,功能与 C++ 标准库的std::shared_ptr<T>(共享指针)类似。当引用计数为 0 且超出作用域之后,SkSurface 对象会自动释放。
sk_sp<T>占用的内存比std::shared_ptr<T>要少,但提供的功能也比std::shared_ptr<T>少。
此行代码执行后,应用程序将在内存中分配 imgInfo 指定的像素空间。也就是说应用程序占用的内存会增加大约 800 * 600 * 4字节。
其中 800 * 600 是图像的尺寸,4 是每个像素的大小(可以简单的理解为A、R、G、B 4个颜色分量,以后我还会详细介绍这方面的知识)。
另外,Skia 是支持硬件加速的(使用显存和 GPU 处理图像),但这里没有用到 Skia 这方面的能力,且如无特殊说明,在后面的文章中也都仅使用 CPU 处理图像(我会在后续文章介绍如何使用 Skia 的硬件加速能力)。
5.SkCanvas* canvas = surface->getCanvas();
用于获得一个 SkCanvas 对象,这个对象提供了一系列用于绘图的 API ,
它通过这些 API 操作 surface 对象管理的像素,在画布上绘图,可以把这个对象理解为画布。
canvas 对象是 SkSurface 的一个属性,多次执行 getCanvas 方法返回的都是同一个 SkCanvas 对象,并不会创建多个 SkCanvas 对象。
canvas 的类型是 SkCanvas* ,这个指针由 surface 对象管理,不用手动释放此对象。
Skia库与大多数C++库一样,不是开发者自己创建的指针,都不需要开发者负责释放。
6.SkPaint paint;
语句初始化了一个 SkPaint 类型的绘图对象。此对象用于描述被绘制元素的附加信息,比如颜色、是否填充、是否抗锯齿等。
7.paint.setColor(SK_ColorGREEN);
设置被绘制元素的颜色为绿色。 SK\_ColorGREEN 是 Skia 内部预定义的颜色。类型为 SkColor
8.SkRect rect;
用于初始化一个矩形对象。
9.rect.setXYWH(10, 10, 100, 100);
用于把矩形的X、Y坐标设置为10,宽度和高度设置为100(正方形是一种特殊的矩形)。
10.canvas->drawRect(rect,paint);
使用paint对象把矩形绘制到画布上。
11.SkPixmap pixmap;
用于初始化一个SkPixmap对象。
SkPixmap 是一个管理 SkImageInfo对象、像素数据地址、每行像素数量的工具,它提供了一系列 API 用来操作和访问像素数据。
SkPixmap 是一个轻量级的类,它并不存储像素数据本身,而仅仅是一个像素数据视图。
12.surface->peekPixels(&pixmap);
用于把 surface 对象管理的像素数组地址、每行像素数量和 SkImageInfo 等信息拷贝到 SkPixmap 对象中。
注意,这里并没有拷贝像素数据,而只是拷贝了像素数据的地址等属性信息。
13.SkFILEWStream stream("allen-image1.png");
用于创建一个文件流对象,这个文件流对象用于写入文件。写入的文件名为allen-image1.png
这里我使用的是文件的相对路径,文件生成后将与应用程序生成的DrawInImage.exe在同一个目录下。
你可以把文件名改成一个文件的绝对路径,来让程序生成的图像文件保存到一个你指定的路径下。
文件路径中最好先不要有中文,目前我还没介绍如何处理中文文件路径的知识。
14.SkPngEncoder::Encode(&stream, pixmap, {});
使用 PNG 编码的方式,把 pixmap 对象描述的像素数据编码到 stream 对象描述的文件流中。
第三个参数是 PNG 编码配置对象 SkPngEncoder::Options ,暂时使用默认值即可。
15.stream.flush()
用于将编码后的数据,从数据缓冲区写入到文件。
现在点击 Visual Studio 运行程序按钮(④处的按钮)执行程序,
程序将一闪而过(很快就退出了),此时out\build\x64-Debug\Debug目录下将生成一个名为allen-image1.png图片,如下图所示:

如果你以Release模式运行程序,那么allen-image1.png图片文件将生成在out\build\x64-Release\Release目录下。

1983

被折叠的 条评论
为什么被折叠?



