当前位置: 首页 > news >正文

Skia-如何渲染文本(上)

渲染简单文本

示例代码如下:

// #include "include/core/SkFontMgr.h"
// #include "include/core/SkFontStyle.h"
// #include "include/ports/SkTypeface_win.h"
// #include "include/core/SkFont.h"
void drawText(SkCanvas *canvas)
{sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_GDI();SkFontStyle fontStyle = SkFontStyle::Normal();sk_sp<SkTypeface> typeFace = fontMgr->matchFamilyStyle("Microsoft YaHei", fontStyle);SkFont font(typeFace,56);SkPaint paint;paint.setColor(0xFF00FFFF);canvas->drawString("Hello World!", 20, 120, font, paint);
}

程序运行结果如下所示:

这段代码完成了以下几项工作:

  1. 创建字体管理器:SkFontMgr

    SkFontMgr_New_GDI 方法负责创建一个基于 GDI 的字体管理器实例。

    该字体管理器使用 Windows GDI 来获取和管理字体。字体管理器的类型为 SkFontMgr ,这个对象可以查找 并使用 Windows 系统上的字体,也可以通过字体文件创建字体(这一点我们后文会介绍)。

    SkFontMgr_New_GDI方法创建的字体管理器仅适用于Windows操作系统,Skia 还提供了很多其他创建字体管理器的方法,读者可以到这里来了解:skia\tools\fonts\FontToolUtils.cpp(TestFontMgr方法,247行附近)。

  2. 创建默认字体样式:SkFontStyle

    字体样式有普通、粗体、斜体、粗斜体之分,SkFontStyle::Normal()负责创建了一个普通字体样式。

  3. 获取字型对象:TypeFace

    SkFontMgr 对象的 matchFamilyStyle 方法通过字体名称( Microsoft YaHei )和字体样式获取一个 TypeFace 字型对象

    这个对象封装了字体、字型等信息。有了它之后,我们就可以创建字体对象了(对应的字体为:微软雅黑)。

  4. 创建字体对象:SkFont

    SkFont 的构造函数接收两个参数,一个是TypeFace对象,一个是字体大小。

  5. 绘制文本

    有了字体对象之后,我们就可以用这个字体绘制文本了。

    SkCanvas 对象的 drawString 方法的第一个参数为被绘制的字符串,第二个和第三个参数为文本绘制的位置。

    第四个参数为字体对象,第五个参数为绘图对象,它让我们绘制的文本呈现蓝色。

如果把 "Hello World!" 改成中文字符串,你会发现这段代码就不工作了。这是为什么呢?

接下来就来解决这个问题。

绘制中文

SkCanvas 对象的 drawString 方法默认使用 UTF8 编码的方式渲染文本,UTF8 是一种变长编码格式,一个字符使用 1 到 4 个字节表示。

比如 A 就占据一个字节的空间, 则占据三个字节的空间,𝄞 则占据四个字节的空间。

UTF16 也是一种变长编码格式,一个字符用 2 到 4 个字符表示。

比如 A 就占据两个字节的空间, 也占据两个字节的空间,𝄞 则占据四个字节的空间。

Windows 系统内大量使用 UTF-16 编码的字符串。

想要让Skia绘制中文就得使用 UTF16 编码字符。

实际上中日韩(技术社区里称之为 CJK 字符)等所有 汉语言体系 的文本都得使用 UTF16 编码来绘制。

下面示例就是三种渲染中文文本的代码:

void drawCJKText(SkCanvas* canvas)
{SkPaint paint;paint.setColor(0xFF00FFFF);paint.setStroke(false);sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_GDI();SkFontStyle fontStyle = SkFontStyle::Normal();sk_sp<SkTypeface> typeFace = fontMgr->matchFamilyStyle("Microsoft YaHei", fontStyle);SkFont font(typeFace, 38);std::wstring text1{ L"你好,世界!" };auto length = text1.size() * sizeof(wchar_t);    canvas->drawSimpleText(text1.c_str(), length, SkTextEncoding::kUTF16, 20, 120, font, paint);auto text2 = wideStrToStr(text1);canvas->drawString(text2.c_str(), 20, 180, font, paint);//canvas->drawSimpleText(text2.c_str(), text2.size(), SkTextEncoding::kUTF8, 20, 180, font, paint);std::u16string text3{ u"你好,世界!" };length = text3.size() * sizeof(char16_t);canvas->drawSimpleText(text3.c_str(), length, SkTextEncoding::kUTF16, 20, 240, font, paint);
}

程序运行结果如下图所示:

渲染CJK文本.png

 第一种方法

此方法使用宽字节字符串渲染文本(这是 Windows 系统中最常用的字符串类型)

宽字节字符串(std::wstring),不同于 std::string ,它内部存储的字符类型为wchar_t,std::string 内部存储的是 char 。

std::wstring 字符串的字节数量为字符数量(text.size())乘以宽字符占据的字节数量( sizeof(wchar_t) 这个值在 Windows 平台和 Linux 平台并不相同)

之所以计算这个长度,是因为在绘制文本时,要把这个长度告知 Skia 库。

我们使用 SkCanvas 对象的 drawSimpleText 方法来绘制宽文本字符串。

这个方法第二个参数即为前面计算的 std::wstring 字符串字节数量,第三个参数为编码方式(UTF16)。

其他参数与 drawString 方法的大同小异,不再赘述。

再来看 第二种方法

此方法先把宽字节字符串(std::wstring)转码为 UTF8 字符串(std::string),再以 UTF8 字符串的方式渲染它。

此处我们使用的 canvas->drawString 方法其内部调用的也是drawSimpleText 方法。

字符串转码方法 wideStrToStr 如下所示:

std::string wideStrToStr(const std::wstring& wstr)
{const int count = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.length(), NULL, 0, NULL, NULL);std::string str(count, 0);WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], count, NULL, NULL);return str;
}

此方法使用 Windows API 把宽字节字符串转型成 UTF8 字符串。

这个工具方法很有用,以后还会用到。

最后看 第三种方法

在此方法中使用 std::u16string 类型来表示 UTF16 字符串。它内部存储的字符类型为 char16_t

所以计算字符串字节数量时,应使用:text3.size() * sizeof(char16_t)

根据项目需要,选择任一种方法绘制 CJK 文本,这三种方法本身并没什么优略之分。

不过有以下几点需要读者注意:

  1. UTF16 编码即可以用来绘制英文,又可以用来绘制中文,甚或中英文混杂的字符串,所以兼容性更好,虽然多占用一些内存,一般也会优先使用这种方式绘制字符串。

  2. 有些字体文件不支持中文,所以当你把编码方式和文本长度都设置好之后,仍然无法绘制中文文本时,那就要看是不是选择了错误的字体了。

文本的位置

文本绘制到坐标 20, 120 处。

你可能觉得这是文本左上角的坐标,实际上不是,这个坐标位于文本右下方。

为绘制的文本添加两条参考线,如下代码所示:

void textPosition(SkCanvas* canvas)
{//... 省略了部分重复的代码paint.setColor(0xFFFFFF00);paint.setStroke(true);paint.setStrokeWidth(1);canvas->drawLine(SkPoint::Make(20, 0), SkPoint::Make(20, h), paint);canvas->drawLine(SkPoint::Make(0, 120), SkPoint::Make(w, 120), paint);//... 省略了部分重复的代码
}

再次运行程序,你会看到两条黄色的参考线,它们的交叉点坐标就是(20, 120)。

文字位置.png

要想精确的控制文本的位置,就必须知道这个点与文本左上角的距离(x_Distancey_Distance)。

http://www.dtcms.com/a/310478.html

相关文章:

  • Android中页面生命周期变化
  • 多人命题系统
  • Qt 开发自动化测试框架搭建
  • 【Open3D】基础操作之三维变换
  • Nginx跨域问题与 MIME 类型错误深度排错指南:解决 MIME type of “application/octet-stream“ 报错
  • 【LeetCode刷题指南】--单值二叉树,相同的树
  • 《人形机器人的觉醒:技术革命与碳基未来》——类人关节设计:柔性驱动革命之液压人工肌肉
  • python中appium
  • 在PyCharm中将现有Gitee项目重新上传为全新项目
  • WordPress 前端显示英文,后台显示中文的设置
  • CH7216A USB Type C上的 DisplayPort 转 HDMI 2.0 转换器【CH7216A-BF】
  • JSON 对象在浏览器中顺序与后端接口返回不一致的问题
  • 基于cygwin或msmy的windows环境下的jupyterlab的C内核搭建
  • Lipschitz连续函数
  • Flutter 替换镜像源
  • 牛客——接头密匙
  • .net依赖注入框架 Autofac和MEF的对比
  • 如何在企业微信中打开外部网页或者自己开发的本地网页
  • vue+ts 基础面试题 (四)
  • 『React』 组件通信全攻略
  • 工业环境中无人叉车安全标准深度解析
  • django的数据库原生操作sql
  • 洛谷做题3:P5711 【深基3.例3】闰年判断
  • 浪潮信息PMO负责人时军受邀为PMO大会主持人
  • 如何最简单、通俗地理解线性回归算法? 线性回归模型在非线性数据上拟合效果不佳,如何在保持模型简单性的同时改进拟合能力?
  • 【C++】类和对象 上
  • JP3-3-MyClub后台后端(二)
  • JavaScript 深拷贝:从基础到完美实现
  • 使用Jeecg低代码平台实现计划管控系统建设方案--1项目前后端搭建
  • 《义龙棒球科普》棒球是韩国的国球吗·棒球1号位