前言
本期将正式给大家介绍Skia的使用。文章将涉及一个目的的多种方案多种情况。文章较长,请大家耐心阅读。
关键词:PNG图像、文件、文本、字符串、UTF8、UTF16、UTF32、编码转换、Visual Studio 设置、高级保存选项
案例
注意:接下来的案例都将使用PNG图像
来存储查看绘制结果
Visual Studio 应将代码文件默认编码设置为UTF-8(建议不带BOM,即标准UTF8编码,操作教程如下:
Visual Studio 设置默认编码格式为 UTF-8 或 GB2312-80 与文件没有高级保存选项怎么显示
这里的常字符串不是专业术语,这里表达的意思是,代码中的字符串以及常量字符串。例如,我们在给一个string
类付初始值时即会使用一个字符串例如"Hello, Skia!"
,这样的字符串是固定的,因为代码中就是这样。因此编译的程序里也一定默认这样初始化。至于常量字符串,则是指const char*
。
绘制代码中字符串步骤和上一期绘制"Hello, Skia!"
一致,这里将详细介绍。
在绘制前,应确保你的Visual Studio项目已经配置好Skia库,具体步骤参考《大海捞针 Skia(C++) 第 1 期:Skia 环境搭建》。
绘制图像,就像画画一样,我们需要一个介质来存储我们的笔画痕迹。因而在Skia
中,我们使用SkBitmap
作为这个介质。
实例化一个SkBitmap
:
SkBitmap bitmap;
好比绘画所用的纸也有规格参数,同样,上述这个介质也需要。用于描述这个参数的类叫做SkImageInfo
。
我们可以通过如下方式,手动指定规格信息,但值得注意的是,生活中,绘画纸的参数是用于描述纸的,而在这里,参数是用于生成纸的,详情将在后面指出。这里,我们创建了一个信息块,同时为其填写信息,即:宽600px、高400px、RGBA颜色,同时设置使用Alpha通道。
SkImageInfo bitmapInfo = SkImageInfo::Make(600, 400, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
接下来我们需要用这个信息分配空间。
bitmap.allocPixels(bitmapInfo);
接下来就是看起来不能理解但很合理的部分。bitmap
作为我们绘制的介质我们却无法直接对其进行绘制,而提供一个SkCanvas
供我们绘制。虽然显得有些繁琐,但其实却很合理。bitmap
仅作为我们存储的地方。而SkCanvas
作为绘制的部分。好比GUI程序界面与其内部数据是分离的,但却不可割舍其中一个。同理,绘制也是如此。SkCanvas
并不存储数据,但却提供给我们绘制的方法;而SkBitmap
不提供绘制方法,却允许我们将图像数据存储其中。这自然是很合理的。
那如何使用SkCanvas
呢?首先,我们知道SkCanvas
是不存储数据的,但提供绘制方法,那我们调用方法绘制时数据存何处?自然是SkBitmap
,因此,我们需要为SkCanvas
绑定一个存储数据的地方——SkBitmap
。如下:
SkCanvas canvas(bitmap);
既然SkCanvas
为我们提供了绘制的方法,那么,我们想要达成绘制字符串的目的,自然需要调用其中的方法。
这里有两个方法可以使用。
第一种,使用drawSimpleText
方法;
第二种,使用drawString
方法。
我相信大家也许去尝试使用这两个方法,然而发现这两个方法有许多参数。不急,我们慢慢解析。
第一种方法原型如下:
void drawSimpleText(const void *text, size_t byteLength, SkTextEncoding encoding,
SkScalar x, SkScalar y, const SkFont &font,
SkPaint &paint);
text:待绘制的字符串
byteLength:字符串字节数
encoding:字符串编码
x:起点横坐标
y:起点纵坐标
font:字体
paint:画笔
绘制文本,自然需要我们给出字符串,那如何表示呢?std::string
?实际上我们可以使用Skia提供的SkString
类存储字符串。这里我们将定义两个字符串,分别用于两种绘制方法。
SkString str_Method1("Drawed by drawSimpleText"),
str_Method2("Drawed by drawString");
第一个参数是字符串指针,我们只需调用SkString
的c_str
方法即可。第二个参数我们同样只需调用SkString
的size
方法即可。第三个参数,因为我们已经设置代码文件编码为UTF8,因此我们只需设置编码为SkTextEncoding::kUTF8
。第四五个参数,我们分别传入绘制起始点的坐标。第六个参数是字体。第七个参数是画笔。
不难发现,这一个方法就带了一堆参数。于是又扯到了字体和画笔上。
接下来,我们将对第六七个参数进行详解。
首先,我们先了解一下字体类——SkFont
。
顾名思义,这个类自然是对字体的设置。实例化一个对象不必多言,但我们如何初始化呢?
方法如下:
SkFont font(SkTypeface::MakeFromName("Consolas", SkFontStyle::Bold()), 20);
这里我们选择使用Consolas
字体,同时设置风格为加粗,大小为20。
接下来就是画笔SkPaint
了,其实字面上看,倒不如说是绘画更为贴切,但Skia使用“Paint”这个名字用来储存绘制动作的属性(包括颜色、透明度等),我们即将其叫做更为熟知的“画笔”。
SkPaint paint;
paint.setColor(SkColor(0xFF000000));
万事俱备,只欠东风。现在我们只需要调用drawSimpleText
方法即可。
canvas.drawSimpleText(str_Method1.c_str(), str_Method1.size(), SkTextEncoding::kUTF8, 0, 20, font, paint);
至于drawString
方法,原型如下:
void drawString(const SkString &str, SkScalar x, SkScalar y, const SkFont& font,
const SkPaint& paint);
看到原型,相比大家已经知道如何使用了。这里不过多解释。
接下来就是输出为PNG图片了。原理很简单,我们只需声明一个文件输出流,然后使用SkEncodeImage
函数编码即可。
SkFILEWStream stream("D:/test.png");
SkEncodeImage(&stream, bitmap, SkEncodedImageFormat::kPNG, 100);
值得注意的是,第四个参数是一个0-100的整数,用来表示图片质量,100为最大,并且,这个参数只会对jpeg和webp格式生效。
参考代码
#include "pch.h"
int main()
{
SkBitmap bitmap;
SkImageInfo bitmapInfo = SkImageInfo::Make(600, 400, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
bitmap.allocPixels(bitmapInfo);
SkCanvas canvas(bitmap);
canvas.clear(0xFFFFFFFF); //注意,这里是ARGB,Alpha通道值在前
SkString str_Method1("Drawed by drawSimpleText"),
str_Method2("Drawed by drawString");
SkFont font(SkTypeface::MakeFromName("Consolas", SkFontStyle::Bold()), 20);
SkPaint paint;
paint.setColor(SkColor(0xFF000000)); //同上
canvas.drawSimpleText(str_Method1.c_str(), str_Method1.size(), SkTextEncoding::kUTF8, 0, 20, font, paint);
canvas.drawString(str_Method2, 0, 50, font, paint);
SkFILEWStream stream("D:/test.png");
SkEncodeImage(&stream, bitmap, SkEncodedImageFormat::kPNG, 100);
return 0;
}