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_top
        int 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及使用技巧的文章;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ControlNet原理及应用

《Adding Conditional Control to Text-to-Image Diffusion Models》 目录 1.背景介绍 2.原理详解 2.1 Controlnet 2.2 用于Stable Diffusion的ControlNet 2.3 训练 2.4 推理 3.实验结果 3.1 定性结果 3.2 消融实验 3.3 和之前结果比较 3.4 数据集大小的影响 4.结…

【C++】进阶模板

非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff1a;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常量来使…

综述:目标检测二十年(机翻版)(未完

原文地址 20年来的目标检测&#xff1a;一项调查 摘要关键词一 介绍二 目标检测二十年A.一个目标检测的路线图1)里程碑&#xff1a;传统探测器Viola Jones探测器HOG检测器基于可变形零件的模型&#xff08;DPM&#xff09; 2)里程碑&#xff1a;基于CNN的两阶段探测器RCNNSPPN…

学会Bitmap内存管理,你的App内存还会暴增吗?

相信伙伴们在日常的开发中&#xff0c;一定对图片加载有所涉猎&#xff0c;而且对于图片加载现有的第三方库也很多&#xff0c;例如Glide、coil等&#xff0c;使用这些三方库我们好像就没有啥担忧的&#xff0c;他们内部的内存管理和缓存策略做的很好&#xff0c;但是一旦在某些…

浅谈C++重载、重写、重定义

C重载、重写、重定义 重载、重写、重定义对比一、重载&#xff08;overload&#xff09;二、重写 / 覆盖&#xff08;override&#xff09;三、重定义 / 隐藏&#xff08;redefining&#xff09; * 为什么在虚函数中不能使用 static 关键字&#xff1f;动态绑定&#xff08;Dyn…

【项目设计】网络版五子棋游戏

文章目录 一、项目介绍1. 项目简介2. 开发环境3. 核心技术4. 开发阶段 二、环境搭建1. 安装 wget 工具2. 更换 yum 源3. 安装 lrzsz 传输工具4. 安装⾼版本 gcc/g 编译器5. 安装 gdb 调试器6. 安装分布式版本控制工具 git7. 安装 cmake8. 安装 boost 库9. 安装 Jsoncpp 库10. 安…

C语言实现冒泡排序(超详细)

排序算法 - 冒泡排序 什么是冒泡排序&#xff1f;冒泡排序有啥用呢&#xff1f;冒泡排序的实现代码讲解冒泡排序的总结 什么是冒泡排序&#xff1f; 冒泡排序是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序…

详谈动态规划问题并解最大子数组和

今天刷力扣又学会了一种算法----动态规划&#xff0c;经过我查阅不少资料后&#xff0c;这些我总结的分享给大家 动态规划是什么&#xff1f; 动态规划&#xff08;Dynamic Programming&#xff09;是一种求解最优化问题的数学方法&#xff0c;它通常用于解决具有重叠子问题和…

802.11-2020协议学习__专题__TxTime-Calculation__HR/DSSS

802.11-2020协议学习__专题__TxTime-Calculation__HR/DSSS 16.2.2 PPDU format16.2.2.1 General16.2.2.2 Long PPDU format16.2.2.3 Short PPDU format 16.3.4 HR/DSSS TXTIME calculation PREV&#xff1a; TBD NEXT&#xff1a; TBD 16.2.2 PPDU format 16.2.2.1 General 定…

快速集成Skywalking 9(Windows系统、JavaAgent、Logback)

目录 一、Skywalking简介二、下载Skywalking服务端三、安装Skywalking服务端3.1 解压安装包3.2 启动Skywalking 四、关于Skywalking服务端更多配置五、Java应用集成skywalking-agent.jar5.1 下载SkyWalking Java Agent5.2 集成JavaAgent5.3 Logback集成Skywalking5.4 集成效果 …

Java Fasn 带您谈谈——开源、闭源

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…

Python —— Mock接口测试

前言 今天跟小伙伴们一起来学习一下如何编写Python脚本进行mock测试。 什么是mock? 测试桩&#xff0c;模拟被测对象的返回&#xff0c;用于测试 通常意义的mock指的就是mock server, 模拟服务端返回的接口数据&#xff0c;用于前端开发&#xff0c;第三方接口联调 为什么…

特征缩放和转换以及自定义Transformers(Machine Learning 研习之九)

特征缩放和转换 您需要应用于数据的最重要的转换之一是功能扩展。除了少数例外&#xff0c;机器学习算法在输入数值属性具有非常不同的尺度时表现不佳。住房数据就是这种情况:房间总数约为6至39320间&#xff0c;而收入中位数仅为0至15间。如果没有任何缩放&#xff0c;大多数…

anaconda安装依赖报错ERROR: Cannot unpack file C:\Users\33659\AppData\Loca...|问题记录

执行命令&#xff1a; # 安装matplotlib依赖 pip install matplotlib-i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com出现问题&#xff1a; ERROR: Cannot unpack file C:\Users\33659\AppData\Local\Temp\pip-unpack-0au_blfq\simple (downloa…

Redis面经

Redis使用场景 1、缓存&#xff1a; 缓存三兄弟(穿透、击穿、雪崩) 、双写一致、持久化、数据过期策略&#xff0c;数据淘汰策略 2、分布式锁 setnx、redisson 3、消息队列 4、延迟队列 何种数据类型&#xff08;list、zset&#xff09; 缓存三兄弟 缓存穿透 缓存穿透…

【RH850芯片】RH850U2A芯片平台Spinlock的底层实现

目录 前言 正文 1.RH850U2A上的原子操作 1.1 Link 1.2 Link generation 1.3 Success in storing 1.4 Failure in storing 1.5 Condition for successful storing 1.6 Loss of the link 1.7 示例代码 2.Spinlock代码分析 2.1 尝试获取Spinlock 2.2 释放Spinlock …

Web前端—移动Web第二天(空间转换、动画、综合案例:全名出游)

版本说明 当前版本号[20231118]。 版本修改说明20231118初版 目录 文章目录 版本说明目录移动 Web 第二天01-空间转换空间转换简介平移视距旋转左手法则rotate3d-了解立体呈现案例-3d导航缩放 02-动画动画实现步骤animation复合属性animation拆分写法案例-走马灯精灵动画多组…

一款带数字传输信号的OVP芯片

基本概述 今天给大家介绍的一款芯片是OVP&#xff0c;相比于传统的OVP芯片来说&#xff0c;这款芯片新增了数字信号控制&#xff0c;可以进行10Mbps的一个通信&#xff0c;通过外部的GPIO口进行控制&#xff0c;达到输入与输出信号的产生。 YHM2009这款OVP芯片具有较低的导通…

图像分类(一) 全面解读复现AlexNet

解读 论文原文&#xff1a;http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf Abstract-摘要 翻译 我们训练了一个庞大的深层卷积神经网络&#xff0c;将ImageNet LSVRC-2010比赛中的120万张高分辨率图像分为1000个不…

如何实现一个下班倒计时程序

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 Hello伙伴们&#xff0c;好几天不见啦。最近也是晚上打球太累&#xff0c;加上一直在研究mybatis的多租户问题&…