Skia如何在图像文件上绘图

从本文开始,我将正式使用 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 的代码,我们会介绍详细一些,后续章节我们将不再逐行介绍代码。

  1. 注释掉的几行代码为需要引入的头文件。在后面的章节中,一个示例可能会涉及到很多代码,分很多段落介绍。所以我尽量以注释的形式告知大家本段代码需要引入什么头文件。至于这些头文件中定义了什么类型,我们会在源码中介绍。
  2. int APIENTRY wWinMain(...)

    这是 Windows 应用程序的入口方法,应用程序启动时,操作系统会首先调用此方法。

  3. SkImageInfo imgInfo = SkImageInfo::MakeN32Premul(800, 600);

    使用指定的宽度和高度创建一个 SkImageInfo 对象。

    SkImageInfo 用于描述图像的基本信息,这个对象除包含图像的宽度、高度信息外,还包含图像的颜色类型、透明度类型等信息(我会在后面的章节介绍这些信息)。

  4. 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目录下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liulun

如果文章真帮到了你,谢谢您打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值