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


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

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

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

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

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

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

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

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

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

            其中坐标点就是位图想要在画布上显示的坐标点,颜色相同的四个方块代表一个像素点,一个方块代表一个字节,则坐标点在画布的缓存中的地址位置为: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的转换。

            有如下代码:

    1. //该代码是从实际项目中摘录,删去了无用代码,导致可能无法编译,仅表示位图生成过程
    2. int Manage::FreeType_Generate_Bitmap(uint32_t h, uint32_t w, std::string color)
    3. {
    4. FT_Library library;
    5. FT_Face face;
    6. FT_GlyphSlot slot;
    7. FT_Error error;
    8. unsigned int co = 0;
    9. // 初始化 FreeType 库
    10. error = FT_Init_FreeType(&library);
    11. if (error)
    12. {
    13. printf("Failed to initialize FreeType library\n");
    14. return 1;
    15. }
    16. // 加载字体文件
    17. error = FT_New_Face(library, "/mmc_data/msyh.ttf", 0, &face);
    18. if (error == FT_Err_Unknown_File_Format)
    19. {
    20. printf("Unsupported font file format\n");
    21. return 1;
    22. }
    23. else if (error)
    24. {
    25. printf("Failed to load font file\n");
    26. return 1;
    27. }
    28. slot = face->glyph;
    29. FT_Select_Charmap(face, FT_ENCODING_UNICODE);
    30. // // 设置字体大小,后面两个参数是长宽,如果其中一个为0,则默认与另一个参数相同
    31. FT_Set_Pixel_Sizes(face, 0, 48);
    32. // 只使用灰度位图
    33. FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL;
    34. // 设置字符串
    35. const char *text = "hello";
    36. // 计算字符串在画布的中间位置
    37. int string_width = 0;
    38. int string_height = 0;
    39. //计算将整个字符串转换为位图后,位图的总长度和总高度
    40. for (int i = 0; i < strlen(text); i++)
    41. {
    42. FT_Load_Char(face, text[i], FT_LOAD_RENDER);
    43. // string_width += face->glyph->bitmap.width;
    44. //计算总长度时要使用水平步长(face->glyph->advance.x),不要使用字符位图宽度(face->glyph->bitmap.width),因为位图是按照
    45. //水平步长或者垂直步长进行偏移的,两者的区别请参考我的文章:
    46. string_width += (face->glyph->advance.x >> 6);
    47. if (face->glyph->bitmap.rows > string_height)
    48. {
    49. string_height = face->glyph->bitmap.rows;
    50. }
    51. }
    52. // 逐字符渲染并绘制到位图
    53. //pen_x,pen_y就是我们想要设置的LCD坐标
    54. int pen_x = (w - string_width) / 2;
    55. int pen_y = string_height - slot->bitmap_top;
    56. FT_Vector pen;
    57. //使用freetype时要乘以64,因为freetype内部是1/64像素
    58. pen.x = pen_x * 64;
    59. pen.y = pen_y * 64;
    60. printf("pen_x : %d, pen_y : %d, top : %d\n", pen_x, pen_y, slot->bitmap_top);
    61. co = Get_Int_Color(color);
    62. for (int i = 0; i < strlen(text); i++)
    63. {
    64. //使用FT_Set_Transform接口将pen_x,pen_y转化为freetype坐标
    65. FT_Set_Transform(face, 0, &pen);
    66. error = FT_Load_Char(face, text[i], FT_LOAD_RENDER);
    67. if (error)
    68. {
    69. printf("Failed to load glyph\n");
    70. continue;
    71. }
    72. //FT_Set_Transform转化之后x坐标为slot->bitmap_left;y坐标为slot->bitmap_top
    73. int x = slot->bitmap_left; // 字形位图的左边界偏移
    74. int y = h - slot->bitmap_top; // 字形位图的上边界偏移,这就是上面说到的坐标转换,再将freetype笛卡尔坐标转换为LCD坐标
    75. FT_Int x_max = x + slot->bitmap.width;
    76. FT_Int y_max = y + slot->bitmap.rows;
    77. //下面就是将位图渲染到LCD的过程,参考上面第二小节
    78. for (int i = x, p = 0; i < x_max; i++, p++)
    79. {
    80. for (int j = y, q = 0; j < y_max; j++, q++)
    81. {
    82. if (i < 0 || j < 0 || i >= w || j >= h)
    83. continue;
    84. lcd_put_pixel(i, j, slot->bitmap.buffer[q * slot->bitmap.width + p], co);
    85. }
    86. }
    87. pen.x += slot->advance.x;
    88. // pen.y += slot->advance.y;
    89. }
    90. // 释放资源
    91. FT_Done_Face(face);
    92. FT_Done_FreeType(library);
    93. return 0;
    94. }

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

  • 相关阅读:
    vue使用swiper轮播组件开启loop模式点击不了问题处理
    Jenkins 基础配置
    统一机器人描述格式——URDF
    云计算之道:学习方法、实践经验与行业展望
    【JVM技术专题】「原理专题」让我们一起探索一下Netty(Java)底层的“零拷贝Zero-Copy”技术(上)
    zookeeper 3.8.1安装和入门使用
    UI自动化测试:Selenium+PO模式+Pytest+Allure整合
    JavaScript-手写every原理
    LCR 056.两数之和 IV
    Ubuntu:安装docker
  • 原文地址:https://blog.csdn.net/qq_39466755/article/details/134483206