freetype将字符串制作成位图并显示过程详解

        在流媒体项目中字幕显示是不可或缺的一环,一般会有字幕流在视频播放过程中进行显示;不过还有很多情况是从头到尾只在视频的某个区域显示某些文字,例如某个电视台的log;这种也称为字幕,如果想要将这些字符串显示到视频,需要将这些字符串做成位图进行显示,无法直接显示,freetype开源库就是将字符转化为位图的工具。

        做了几个freetype的项目之后总结的几个难点:

1.utf8或者gb2312等中文如何生成位图

        画布和freetype是无法处理中文编码的,像GB2312或UTF8等中文编码需要转换成unicode编码才能够被处理,显示;字符串的编码转换可以使用iconv库进行编码转换,想要了解更多iconv库的详细情况,关注我后期持续更新。

2.生成的位图如何显示到画布上;(核心点,最难点)

        首先明确一点:freetype只是将字符做成位图的工具,不涉及位图在画布显示相关的配置;具体如何将位图显示到LCD(嵌入式称为画布)?

        利用c/c++的话术来说就是将保存位图的缓存内容拷贝到画布的缓存上即可。但是不仅仅是简单的字符拷贝,在拷贝的过程中还要考虑到LCD的像素格式(RGB888,RGB555等),不同的像素格式,单个像素所占的内存大小是不一样的,就导致拷贝时的内存偏移是不同的;下面代码进行跟明确的解释:

//这里LCD的像素格式为ARGB8888,即一个像素占四个字节,A:透明度(8bit),R:红色(8bit),G:绿色(8bit),B:蓝色(8bit)
void Manage::lcd_put_pixel(int x, int y, unsigned int pixel, unsigned int color)
{/**Get_Canvas_Addr():画布的地址;*(32 / 8):一个像素占多少字节;*Get_Stride():垂直步长,和像素格式有关,假如画布的分辨率为:1920*1080;像素格式为ARGB8888,则垂直步长为1920*4*              很好理解:画布水平有1920个像素点,每个像素点占四个字节;*/unsigned char *pen_8 = (Get_Canvas_Addr() + x * (32 / 8) + y * Get_Stride());//画布显示位置在画布缓存中的地址偏移unsigned int *pen_16;unsigned int red = 0, green = 0xff, blue = 0;pen_16 = (unsigned int *)pen_8;//pixel:代表像素点的值if (pixel != 0){ //一个像素由RGB组成,而像素格式占四个字节,所以将unsigned char类型的缓存转换成unsigned int类型进行赋值,代表整个像素点pixel = color;}else{//没有像素点说明是空白区域,以白色填充*pen_16 = 0x0000;}//将像素值拷贝到画布*pen_16 = pixel;
}

        如果对于画布的坐标偏移很难理解,参考下下图:

        其中坐标点就是位图想要在画布上显示的坐标点,颜色相同的四个方块代表一个像素点,一个方块代表一个字节,则坐标点在画布的缓存中的地址位置为:y*(1920*4)+x*4; 这样就很清楚明了了吧!

        这个模块就是将生成好的位图数据显示到LCD画布的过程,在实际的项目开发中画布的坐标,画布的分辨率,画布的像素格式,位图的坐标,位图的各种参数都是非常重要的考虑项;想要了解更多关注我后续持续更新;

3.LCD坐标和笛卡尔坐标在实际项目中如何应用

        lcd坐标:就是LCD在做图像显示时的坐标,以左上角为原点,y轴与笛卡尔坐标相反向下为正,x轴与笛卡尔坐标一样;

        笛卡尔坐标:就是我们上学时学到坐标轴;

        为什么要说这两个坐标的?因为如果坐标与freetype接口相关的话就会涉及到笛卡尔坐标,因为freetype内部使用的时笛卡尔坐标,需要与LCD坐标进行转换。

        具体分析图如下:

        在进行位图渲染时,freetype是按照红点为原点进行坐标设置的,而LCD(画布)是按照绿色原点进行设置的,因此如果坐标使用freetype进行设置,那么y坐标要使用LCD的高减去想要的y值才是我们想要的坐标;

        如上图,想要让位图显示在LCD(100,100)的坐标处,但是使用freetype时y坐标就不能设置为100,而要设置为h-100;x都是一样的,不用考虑x的转换。

        有如下代码:

//该代码是从实际项目中摘录,删去了无用代码,导致可能无法编译,仅表示位图生成过程
int Manage::FreeType_Generate_Bitmap(uint32_t h, uint32_t w, std::string color)
{FT_Library library;FT_Face face;FT_GlyphSlot slot;FT_Error error;unsigned int co = 0;// 初始化 FreeType 库error = FT_Init_FreeType(&library);if (error){printf("Failed to initialize FreeType library\n");return 1;}// 加载字体文件error = FT_New_Face(library, "/mmc_data/msyh.ttf", 0, &face);if (error == FT_Err_Unknown_File_Format){printf("Unsupported font file format\n");return 1;}else if (error){printf("Failed to load font file\n");return 1;}slot = face->glyph;FT_Select_Charmap(face, FT_ENCODING_UNICODE);// // 设置字体大小,后面两个参数是长宽,如果其中一个为0,则默认与另一个参数相同FT_Set_Pixel_Sizes(face, 0, 48);// 只使用灰度位图FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL;// 设置字符串const char *text = "hello";// 计算字符串在画布的中间位置int string_width = 0;int string_height = 0;//计算将整个字符串转换为位图后,位图的总长度和总高度for (int i = 0; i < strlen(text); i++){FT_Load_Char(face, text[i], FT_LOAD_RENDER);// string_width += face->glyph->bitmap.width;//计算总长度时要使用水平步长(face->glyph->advance.x),不要使用字符位图宽度(face->glyph->bitmap.width),因为位图是按照//水平步长或者垂直步长进行偏移的,两者的区别请参考我的文章:string_width += (face->glyph->advance.x >> 6);if (face->glyph->bitmap.rows > string_height){string_height = face->glyph->bitmap.rows;}}// 逐字符渲染并绘制到位图//pen_x,pen_y就是我们想要设置的LCD坐标int pen_x = (w - string_width) / 2;int pen_y = string_height - slot->bitmap_top;FT_Vector pen;//使用freetype时要乘以64,因为freetype内部是1/64像素pen.x = pen_x * 64;pen.y = pen_y * 64;printf("pen_x : %d, pen_y : %d, top : %d\n", pen_x, pen_y, slot->bitmap_top);co = Get_Int_Color(color);for (int i = 0; i < strlen(text); i++){//使用FT_Set_Transform接口将pen_x,pen_y转化为freetype坐标FT_Set_Transform(face, 0, &pen);error = FT_Load_Char(face, text[i], FT_LOAD_RENDER);if (error){printf("Failed to load glyph\n");continue;}//FT_Set_Transform转化之后x坐标为slot->bitmap_left;y坐标为slot->bitmap_topint x = slot->bitmap_left;    // 字形位图的左边界偏移int y = h - slot->bitmap_top; // 字形位图的上边界偏移,这就是上面说到的坐标转换,再将freetype笛卡尔坐标转换为LCD坐标FT_Int x_max = x + slot->bitmap.width;FT_Int y_max = y + slot->bitmap.rows;//下面就是将位图渲染到LCD的过程,参考上面第二小节for (int i = x, p = 0; i < x_max; i++, p++){for (int j = y, q = 0; j < y_max; j++, q++){if (i < 0 || j < 0 || i >= w || j >= h)continue;lcd_put_pixel(i, j, slot->bitmap.buffer[q * slot->bitmap.width + p], co);}}pen.x += slot->advance.x;// pen.y += slot->advance.y;}// 释放资源FT_Done_Face(face);FT_Done_FreeType(library);return 0;
}

        当然,如果你觉得坐标的相互转换很迷惑,我这有一种方可可以只考虑LCD坐标,不考虑笛卡尔坐标,也就是坐标不经过freetype;具体方法关注我,后期会出一篇详解freetype及使用技巧的文章;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/147220.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Page分页records有数据,但是total=0,解决办法

Page分页records有数据&#xff0c;但是total0&#xff0c;解决办法 问题&#xff1a;程序运行起来后&#xff0c;后端接收前端传来的搜索请求信息正常&#xff0c;但无法在前端正确反馈信息&#xff0c;通过在后端排查发现total一直等于零&#xff0c;但数据库中有数据&#x…

jenkins清理缓存命令

def jobName "yi-cloud-operation" //删除的项目名称 def maxNumber 300 // 保留的最小编号&#xff0c;意味着小于该编号的构建都将被删除 Jenkins.instance.getItemByFullName(jobName).builds.findAll { it.number < maxNumber }.each { it.delet…

微服务和Spring Cloud Alibaba介绍

1、微服务介绍 1.1 系统架构演变 随着互联网的发展&#xff0c;网站应用的规模也在不断的扩大&#xff0c;进而导致系统架构也在不断的进行变化。从互联网早起到现在&#xff0c;系统架构大体经历了下面几个过程: 单体应用架构 —> 垂直应用架构 —> 分布 式架构—>…

聊一聊前端面临的安全威胁与解决对策

前端是用户在使用您的网站或Web应用程序时首先体验到的东西。如果您的Web应用程序的前端受到侵害&#xff0c;它可能会影响整个布局&#xff0c;并造成糟糕的用户体验&#xff0c;可能难以恢复。集成前端安全变得越来越重要&#xff0c;本文将指导您通过可以应用于保护您的Web应…

[nlp] 损失缩放(Loss Scaling)loss sacle

在深度学习中,由于浮点数的精度限制,当模型参数非常大时,会出现数值溢出的问题,这可能会导致模型训练不稳定。为了解决这个问题,损失缩放(Loss Scaling)技术被引入,它通过缩放损失值来解决这个问题。 在深度学习中,损失缩放技术通常是通过将梯度进行缩放来实现的。具…

vue --version无法显示,只弹出vs窗口

参考连接&#xff1a; nodejs环境配置&#xff08;解压包&#xff09;安装教程_nodejs解压版安装及环境配置_tubond的博客-CSDN博客 原因&#xff1a;环境没搞好&#xff0c;没有设置全局文件夹&#xff0c;node默认放在C盘了&#xff0c;C盘有权限。因为npm -i vue/cli创建…

文章分类列表进行查询(实体类日期格式设置)

categoryController GetMappingpublic Result<List<Category>> list(){List<Category> cs categoryService.list();return Result.success(cs);} categoryService //列表查询List<Category> list(); categoryServiceImpl Overridepublic List<Cat…

CI/CD相关概念学习

文章目录 CI/CD相关概念学习前言CI/CD相关概念介绍集成地狱持续集成持续交付持续部署Devops CI/CD相关应用介绍JenkinsTekton PipelinesSpinnakerTravis CIGoCD CI/CD相关概念学习 前言 本文主要是介绍一些 CI/CD 相关的概念&#xff0c;通过阅读本文你将快速了解 CI/CD 是什么…

Flutter NestedScrollView 、SliverAppBar全解析,悬浮菜单的应用

在我们开发过程中经常会使用到悬浮菜单的使用&#xff0c;当我们滑动到指定位置后&#xff0c;菜单会自动悬浮。 实现效果如下&#xff08;左为滑动前、右为滑动后&#xff09;&#xff1a; 上述便是通过NestedScrollView 、SliverAppBar实现的效果&#xff0c;通过两个控件我…

1.rk3588的yolov5运行:pt_onnx_rknn转换及rknn在rk3588系统python运行

自己有点笨&#xff0c;查资料查了一周才完美的实现了yolov5在rk3588环境下的运行&#xff0c;在这里写具体步骤希望大家少走弯路。具体步骤如下&#xff1a; 一、yolov5的原代码下载及pt文件转换为onnx文件 1.yolov5的原代码下载及环境搭建 在这里一定要下载正确版本的源代码…

模拟实现一个Linux中的简单版shell

exec系列接口中的环境变量 在之前我们学习了exec系类函数的功能就是将一个程序替换成另外一个程序。 然后就会出现下面的问题&#xff1a; 首先父进程对应的环境变量的信息是从bash中来的&#xff0c;因为我们自己写的父进程在运行的时候首先就要成为bash的子进程。这里我们将…

9 STM32标准库函数 之 独立看门狗(IWDG)所有函数的介绍及使用

9 STM32标准库函数 之 独立看门狗&#xff08;IWDG&#xff09;所有函数的介绍及使用 1. 图片有格式该文档修改记录&#xff1a;总结 函数描述格式&#xff1a; 函数名外设函数的名称函数原形原形声明功能描述简要解释函数是如何执行的输入参数{x}输入参数描述输出参数{x}输出…

单片机语音芯片在工业控制中的应用优势

单片机语音芯片&#xff0c;这一智能化的代表产品&#xff0c;不仅在家庭和消费电子领域发挥着重要的作用&#xff0c;更为工业控制领域注入了新的活力。将单片机语音芯片与语音交互技术相结合&#xff0c;为工业设备的控制和监测提供了前所未有的解决方案。 首先&#xff0c;…

人充当LLM Agent的工具(Human-In-The-Loop ),提升复杂问题解决成功率

原文&#xff1a;人充当LLM Agent的工具&#xff08;Human-In-The-Loop &#xff09;&#xff0c;提升复杂问题解决成功率 在Agent开发过程中&#xff0c;LLM充当Agent的大脑&#xff0c;对问题进行规划、分解、推理&#xff0c;在执行过程中合理选择利用工具&#xff08;Tool&…

Uniapp连接iBeacon设备——实现无线定位与互动体验(实现篇)

export default { data() { return { iBeaconDevices: [], // 存储搜索到的iBeacon设备 deviceId: [], data: [], url: getApp().globalData.url, innerAudioContext: n…

计算机网络的体系结构

目录 一. 计算机体系结构的形成二. 协议与层次划分2.1 数据传输过程2.2 什么是网络协议2.3 网络协议的三要素2.4 协议有两种形式2.4 各层协议2.5 什么是复用和分用 \quad 一. 计算机体系结构的形成 \quad 计算机网络是一个非常复杂的系统, 相互通信的两个计算机系统必须高度协调…

SSH协议简介与使用

Secure Shell(SSH) 是由 IETF(The Internet Engineering Task Force) 制定的建立在应用层基础上的安全网络协议。它是专为远程登录会话(甚至可以用Windows远程登录Linux服务器进行文件互传)和其他网络服务提供安全性的协议&#xff0c;可有效弥补网络中的漏洞。通过SSH&#xf…

本地jar导入maven

一、通过dependency引入 1.1. jar包放置&#xff0c;建造lib目录 1.2. pom.xml文件 <dependency><groupId>zip4j</groupId><artifactId>zip4j</artifactId><version>1.3.2</version><!--system&#xff0c;类似provided&#x…

网络原理-IP/数据链路层协议

一. IP IP协议有两个版本,IPv4和IPv6.我们通常所用的IP协议,若没有特殊说明,默认都是IPv4. IPv4数量2^32,大约43亿左右,而TCP/IP协议规定,每个主机都需要有一个IP地址.对于全世界的计算机来说,这个数量是不够的,所以后来推出了IPv6(长度128位,是IPv4的4倍).但因为目前IPv4还广…

leetcode栈和队列三剑客

用队列实现栈 队列是先进先出的&#xff0c;而栈是只能在栈顶进行出栈和入栈&#xff0c;那我们这道题要用队列来实现栈的话&#xff0c;这里给的思路是两个队列&#xff0c;因为两个队列的话就可以相互导数据&#xff0c;比如我们来实现这个题目的push函数&#xff0c;我们的栈…