[ARM-2D 专题]6.脏矩形定义的宏使用技巧和分析

embedded/2024/11/14 3:33:40/

在这里插入图片描述
ARM-2d之所以能够高效的进行屏幕绘制,脏矩形的使用起到了巨大作用,功不可没。
简单介绍一下何谓脏矩形:
在这里插入图片描述
详细可以参考:如何用脏矩形优化显示帧率
在一帧画面的绘制的时候,我们只绘制画面中变化的部分,可以大大的减少显示屏幕刷新带来的数据传送的工作量,从而提高绘制效率。在资源及其贫乏和受限的嵌入式小系统中,要实现非常多的特效(比如局部滚动,局部动画,旋转,淡入淡出等),使用这个技巧和功能是必不可少。
要使用这个功能,我们必须指出当前画面中的那些区域是需要更新的(第一次刷新除外),也就是定义好脏矩形,从而接下来的相关API在绘制图形的时候,才会对区域进行剪切,通过一些列复杂的计算,得到最终需要绘制的区域。
ARM2D提供了几个相关的宏来帮助我们简单的定义一个脏矩形链表,从而提供给场景播放器使用:

//定义一个脏矩形数组
#define IMPL_ARM_2D_REGION_LIST(__NAME, ...)                                    \__IMPL_ARM_2D_REGION_LIST(__NAME,##__VA_ARGS__)
//增加一个脏矩形            
#define ADD_REGION_TO_LIST(__NAME, ...)                                         \__ADD_REGION_TO_LIST(__NAME, ##__VA_ARGS__) 
//增加最后一个脏矩形(如果只有一个,那就只用这个宏增加脏矩形)            
#define ADD_LAST_REGION_TO_LIST(__NAME, ...)                                    \__ADD_LAST_REGION_TO_LIST(__NAME, ##__VA_ARGS__)      
//结束脏矩形定义            
#define END_IMPL_ARM_2D_REGION_LIST(...)                                        \};                   

我们来看看使用这几个宏定义一个脏矩形数组的一个完整例子:

/*! define dirty regions */
IMPL_ARM_2D_REGION_LIST(s_tDirtyRegions, static)/* a dirty region to be specified at runtime*/ADD_REGION_TO_LIST(s_tDirtyRegions,0,),ADD_REGION_TO_LIST(s_tDirtyRegions,.tLocation = {.iX = 0,.iY = 0,},.tSize = {.iWidth = 0,.iHeight = 8,},),   /* add the last region:* it is the top left corner for text display */ADD_LAST_REGION_TO_LIST(s_tDirtyRegions,.tLocation = {.iX = 0,.iY = 0,},.tSize = {.iWidth = 0,.iHeight = 8,},),END_IMPL_ARM_2D_REGION_LIST(s_tDirtyRegions)

这几个宏的使用,简化了我们定义脏矩形的工作,但是同时,对于初次接触的用户,也不免有点云里雾里的感觉,同时也比较好奇,它究竟干了什么工作?是怎么实现的呢?下面我们就来个庖丁解牛,一刀一刀的解剖一下,揭开庐山真面目:
先看第一个定义脏矩形的宏的实现:

#define __IMPL_ARM_2D_REGION_LIST(__NAME, ...)                                  \enum {                                                              \__NAME##_offset = __COUNTER__,                                  \};                                                                  \__VA_ARGS__                                                         \arm_2d_region_list_item_t __NAME[] = {#define IMPL_ARM_2D_REGION_LIST(__NAME, ...)                                    \__IMPL_ARM_2D_REGION_LIST(__NAME,##__VA_ARGS__)

如果我们把上面例子代码IMPL_ARM_2D_REGION_LIST(s_tDirtyRegions, static)展开,就得到如下的代码:

  enum {                                                              \s_tDirtyRegions_offset = __COUNTER__,                            \};                                                                  \static arm_2d_region_list_item_t s_tDirtyRegions[] = {

这个是不是没有任何奇特的地方,搞C语言的老铁们,就一眼看明白了,它就是声明了一个arm_2d_region_list_item_t类型的static类型的数组(“开头部分”)。
这里唯一的一个技巧就是使用了一个我们并不常见和多用的一个编译器的预定义宏__COUNTER__。
__COUNTER__是什么?简单科普一下:

  1. 基本概念:__COUNTER__是 GNU 编译器(如 GCC)的一个非标准扩展特性,它代表一个整数值,初始值通常为0,每遇到一次该宏就会自动加1
  2. 使用场景:这个宏常被用来防止重复声明冲突,例如在定义多个模板实例或类时,可以通过结合 ## 运算符来生成唯一的名称,从而避免命名冲突
  3. 唯一标识符:COUNTER 可以与 ## 运算符配合使用,以构建唯一的标识符。例如,可以将任意一个标识符与 COUNTER
    合并,形成一个新的唯一标识符
  4. 注意事项:在使用 COUNTER 时,需要确保它不会在包含预编译头文件之前被展开,否则预编译的头文件将不会被使用

好啦,了解了上面的硬核知识,后,上面的定义我们进一步展开就得到如下代码(假定编译到这里的此时此刻,COUNTER=100)

  enum {                                                            s_tDirtyRegions_offset = 100,                            };                                                                  static arm_2d_region_list_item_t s_tDirtyRegions[] = {

至此,我们得到一个匿名枚举类型的定义,s_tDirtyRegions_offset =100,这个常量的作用,在后面形成链表的时候起到至关重要的作用,具体应用暂且不表,我们继续看看增加一个脏矩形元素到数组的宏是如何工作的:
增加一个脏矩形的宏ADD_REGION_TO_LIST定义如下:

#define __ADD_REGION_TO_LIST(__NAME, ...)                                       \{                                                                   \.ptNext = (arm_2d_region_list_item_t *)                         \&(__NAME[__COUNTER__ - __NAME##_offset]),           \.tRegion = {                                                    \__VA_ARGS__                                                 \},                                                              \}#define ADD_REGION_TO_LIST(__NAME, ...)                                         \__ADD_REGION_TO_LIST(__NAME, ##__VA_ARGS__) 

我们将例子中的宏"ADD_REGION_TO_LIST(s_tDirtyRegions,0,),"展开就得到如下的代码:

  {                                                          .ptNext = (arm_2d_region_list_item_t *) &(s_tDirtyRegions[__COUNTER__ - s_tDirtyRegions_offset ]),.tRegion = {0,},                                  },

我们利用前面的__COUNTER__知识知道,这个宏被展开的时候,__COUNTER__会等于101(前面假定了脏矩形定义中展开的时候等于100),那么动物们就可以求出来这个常量:COUNTER - s_tDirtyRegions_offset =1,从而上面的代码被转换为:

  {                                                          .ptNext = (arm_2d_region_list_item_t *) &(s_tDirtyRegions[1]),.tRegion = { 0,},                                  },

这段代码的意思就是将素组的第一个arm_2d_region_list_item_t元素的ptNext指针,指向了该数组的第二个元素的其实地址,形成一个单向链表,同时,将tRegion按照传入的参数初始化。
如果有多个脏矩形需要添加,继续使用宏ADD_REGION_TO_LIST添加即可。
最后来看看脏矩形的最后一个元素的添加宏ADD_LAST_REGION_TO_LIST的定义:

#define __ADD_LAST_REGION_TO_LIST(__NAME, ...)                                  \{                                                                   \.ptNext = NULL,                                                 \.tRegion = {                                                    \__VA_ARGS__                                                 \},                                                              \}#define ADD_LAST_REGION_TO_LIST(__NAME, ...)                                    \__ADD_LAST_REGION_TO_LIST(__NAME, ##__VA_ARGS__) 

同样的方法,我们展开该宏,得到如下代码:

  {                                                          .ptNext = NULL,.tRegion = {.tLocation = {.iX = 0,.iY = 0,},.tSize = {.iWidth = 0,.iHeight = 8,},},                                  },

该宏和ADD_REGION_TO_LIST的唯一区别就是对ptNext 指针的初始化,因为后面没有任何元素了,所以ptNext被初始化为NULL。
元素的添加完成了,数组的最后收尾就比较简单了,END_IMPL_ARM_2D_REGION_LIST(s_tDirtyRegions)完成最后的定义闭环,宏定义如下:

#define END_IMPL_ARM_2D_REGION_LIST(...)                                        \};

将END_IMPL_ARM_2D_REGION_LIST(s_tDirtyRegions)展开后就简化为:

};

我们将前面几个宏的展开代码合并到一起,就看到了一个完整的素组定义:

  enum {                                                            s_tDirtyRegions_offset = 100,                            };                                                                  static arm_2d_region_list_item_t s_tDirtyRegions[] = {{                                                          .ptNext = (arm_2d_region_list_item_t *) &(s_tDirtyRegions[1]),.tRegion = { 0,},                                  },{                                                          .ptNext = NULL,.tRegion = {.tLocation = {.iX = 0,.iY = 0,},.tSize = {.iWidth = 0,.iHeight = 8,},},                                  },};

至此,我们利用几个宏,非常简单的定义出来了一个脏矩形数组,并且形成了一个单向链表,后面将会提供给场景使用。
arm2d里面,充分的利用宏的技巧,简化了非常多的工作,并且用C语言的方式,实现了优雅和完美的OOPC的编程,后续章节我们会陆续解密,敬请关注!
原创文章,欢迎转载,请注明出处,未经书面允许,不得用于商业用途


http://www.ppmy.cn/embedded/136918.html

相关文章

前向-后向卡尔曼滤波器(Forward-Backward Kalman Filter)资料汇总

《卡尔曼滤波引出的RTS平滑》参考位置2《卡尔曼滤波系列——(六)卡尔曼平滑》《关于卡尔曼滤波和卡尔曼平滑关系的理解》——有m语言例程《Forward Backwards Kalman Filter》——Matlab软件《卡尔曼滤波与隐马尔可夫模型》

《揭秘观察者模式:作用与使用场景全解析》

在软件开发的世界中,设计模式就像是建筑师手中的蓝图,指导着软件系统的构建。其中,观察者模式是一种极为重要且广泛应用的设计模式。今天,我们就来深入探讨一下观察者模式的作用和使用场景。 一、观察者模式是什么? …

昇思大模型平台打卡体验活动:项目8基于MindSpore实现ChatGLM4聊天机器人

进入大模型平台,只需要运行即可,可以改输入。 ChatGLM4:使用MindSpore进行聊天机器人开发 本文档提供了使用MindSpore和MindNLP部署和推理ChatGLM4聊天机器人模型的详细步骤,帮助用户搭建一个可以与ChatGLM4互动的对话系统。 前…

Qt 项目架构设计

在开发一个 Qt 项目时,合理的文件夹结构和清晰的构建流程是非常重要的。Qt 项目通常需要管理源代码、UI 文件、资源文件、构建脚本等。下面我会给出一个详细的文件夹结构示例,并解释每个部分的作用及如何设计 Makefile 或使用 Qt 的 qmake 来自动化构建过…

【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)

关键词:一多、响应式、媒体查询、栅格布局、断点、UI 随着设备形态的逐渐增多,应用界面适配也面临着很大问题,在以往的安卓应用开发过程中,往往需要重新开发一套适用于大屏展示的应用,耗时又耗力,而鸿蒙提供…

int socket(int domain,int type,int protocol);

本文内容产生自智谱清言 头文件&#xff1a; #include <sys/socket.h> int socket(int domain,int type,int protocol); 它是在C语言中使用的一个系统调用函数&#xff0c;用于创建一个新的套接字。套接字是支持TCP/IP协议的网络通信的端点&#xff0c;可以看作是不同…

ubuntu ros 解决建完图后 保存的地图非常小的问题

解决建完图后 保存的地图非常小的问题 在ROS中使用Gmapping等SLAM算法建图后&#xff0c;如果保存的地图非常小&#xff0c;通常是由于建图过程中的分辨率设置不当或地图边界没有覆盖到整个环境导致的。以下是详细的解决方案和具体步骤&#xff1a; 解决方案概述 调整地图分…

阿里云aliyun gradle安装包下载地址

阿里云 查找你要下载的安装包 macports-distfiles-gradle安装包下载_开源镜像站-阿里云 https://mirrors.aliyun.com/macports/distfiles/gradle/gradle-8.9-bin.zip 腾讯 https://mirrors.cloud.tencent.com/gradle/ https://mirrors.cloud.tencent.com/gradle/ https…