16

傻孩子(GorgonMeducer) · 2022年02月07日

【玩转Arm-2D】如何制作一个酷炫的表盘

以下文章来源于嵌入式小书虫 ,作者FledgingSu 支离苏

前两篇我们讲完了Arm-2D Tile的用法,有了Tile与子Tile的思想,我们也玩一下Arm-2D的高级功能rotation(旋转)和抗锯齿。

说到旋转,我首先就想到了仪表指针的旋转,好那我们就用Arm-2D制作一个自己的智能手表,并给他开启美颜功能。

啥,还有美颜。

对啊,Arm-2D的旋转函数为我们提供了抠图功能(masking)、半透明显示的透明度功能(Opacity),最重要的还提供了抗锯齿功能,这3大功能用好了可不就相当于开启美颜了。

我们要实现的智能手表效果如下图所示:
image.png

【旋转接口函数简介】

好,那我们先看看Arm-2D旋转的接口函数,如下

arm_2dp_rgb565_tile_rotation(   (arm_2d_op_rotate_t *)&(_->tOP),
    ptItem->ptTile,     //!< source tile
    ptTile,             //!< target tile
    &(ptItem->tRegion), //!< target region
    ptItem->tCentre,    //!< center point
    ptItem->fAngle,     //!< rotation angle
    GLCD_COLOR_BLACK,   //!< masking colour
    ptItem->ptTargetCentre);
  • 第1个参数tOP是Arm-2D自己用的,我们不用管
  • 第2个参数source Tile就是我们要旋转的表针(Tile)

如下图所示:
image.png

  • 第3个参数为target Tile(即把source Tile贴到哪)
  • 第4个参数target region就是表针旋转之后要显示到目标Tile的哪个区域,如下图所示:
    image.png
  • 注意:这个target region的作用是规定旋转后的图片贴到目标Tile的什么位置(超出部分会被裁剪掉)

有了target Tile和source Tile,那就该设置旋转中心了,即设置第5个参数center point和最后一个参数ptTargetCentre,如下图所示:
image.png

  • (11,49)为center point,注意:不一定是source Tile中心点哦
  • 注意:ptTargetCentre为NULL时,默认就是目标区域的中心点,ptTargetCentre也可以指定 为目标区域的任意一个坐标点,还是很灵活的。

设置好之后如下图:
image.png

  • 怎么回事?明明一个旋转中心,怎么设置了两个点。

哈哈哈,这就是Arm-2D设计的优点,你可以把source Tile和target Tile想象成两个纸片,旋转就好比是用一个图钉把两个纸片钉在一起,然后source Tile绕着图钉在 target Tile上旋转。此时,当图钉穿过source Tile和target Tile的时候,会在两个纸片上分别留下一个洞——这个洞就是source Tile和target Tile上各自的旋转圆心。

  • 其实有这两个旋转圆心也就确定了source Tile在target Tile上的初始位置(即旋转0度角的位置)。

接下来就该旋转了,即设置旋转角度rotation angle,也就是第6个参数,如下图:
image.png

  • 第6个参数rotation angle为float类型,且单位为弧度,大家记得把角度转换成弧度哦。

第7个参数masking colour就是大名鼎鼎的抠图功能,

Arm-2D的抠图功能,

是不是觉得很厉害,可以指定一种颜色不显示,如下图所示:
image.png

  • masking color设置为黑色,也就是source Tile中的黑色都不会被显示出来。

如果你觉得masking color的美颜效果还不够好,Arm-2D还给我们提供了透明度的效果,接口函数如下:

arm_2dp_rgb565_tile_rotation_with_alpha(&(ptItem->tOP),
  ptItem->ptTile,     //!< source tile
  ptTile,             //!< target tile
  &(ptItem->tRegion), //!< target region
  ptItem->tCentre,    //!< center point
  ptItem->fAngle,     //!< rotation angle
  GLCD_COLOR_BLACK,   //!< masking colour
  ptItem->chOpacity,  //!< Opacity
  ptItem->ptTargetCentre);
  • 第8行多了一个Opacity(不透明度)参数,注意是不透明度,取值范围是0~255,也就是0为全透明,255为不透明。

其实,旋转最主要的还是锯齿问题,那Arm-2D有提供抗锯齿吗?

答案是肯定的,

而且开启抗锯齿还很简单,只需要设置一个宏开关,如下

#define __ARM_2D_HAS_INTERPOLATION_ROTATION__  1
  • 注意:开启抗锯齿对单片机的算力还是有一定的要求,建议M4及以上的单片机开启抗锯齿,否则速度会很慢。

【程序实现】

现在我们就用Arm-2D实现我们的智能手表。

首先背景要制作一个好看的图片,如下

arm_2d_rgb16_tile_copy( &c_tilebackground, 
      ptTile, 
      &tBox,                            
      ARM_2D_CP_MODE_COPY); 
  • 用Tile copy显示一张背景图片。

接下来就该使用旋转函数进行表针旋转了,我们先定义一个结构体,存放要给旋转函数传的参数,如下:

typedef struct {
    arm_2d_op_rotate_opacity_t tOP;
    const arm_2d_tile_t *ptTile;//source Tile
    float fAngle;//旋转角度
    float fAngleSpeed;//旋转速度
    arm_2d_location_t tCentre;//source Tile的圆心
    arm_2d_location_t *ptTargetCentre;//Target Tile的圆心
    arm_2d_region_t tRegion;//旋转区域
    uint8_t chOpacity;//不透明度
} rotation_watch_hand_t;

接下来用这个结构体类型定义一个数组,包含3个元素,即时针、分针、秒针,我以时针为例,如下:

static rotation_watch_hand_t s_tGears[] = {
  {
    //source Tile
    .ptTile = &c_tilePointerhour,
    //旋转速度
    .fAngleSpeed = 0.1f/60.f,
    //source Tile的圆心
    .tCentre = {
        .iX = 11,
        .iY = 49,
    },
    //旋转区域    
    .tRegion = {
        .tLocation = {
            .iX = 23,
            .iY = 30,
        },
        .tSize = {
            .iWidth = 200,
            .iHeight = 200,
        },
    },
    #if 0  /*! a demo shows how to specifiy the centre of rotation on the target tile */
        .ptTargetCentre = (arm_2d_location_t []){
            {
                .iX = ((APP_SCREEN_WIDTH - 41) >> 1) + 60,
                .iY = ((APP_SCREEN_HEIGHT - 41) >>1) + 60,
            },
        },
    #endif
    //不透明度
    .chOpacity = 205,
  },
};
  • 注意23~30行,我们没有设置Target Tile的圆心,默认为旋转区域的中心。也可以指定(iX,iY)的值设定Target Tile的圆心。

好了,旋转就很简单了,直接调用函数就可以了,如下:

void watch_hand_rotation(const arm_2d_tile_t *ptTile, bool bIsNewFrame)
{            
    /*! for each item (ptItem) inside array s_tGears */
    arm_foreach (demo_gears_t, s_tGears, ptItem) {
      //更新旋转角度 
      if (bIsNewFrame) {
        //角度要转换成弧度            
        ptItem->fAngle += ARM_2D_ANGLE(ptItem->fAngleSpeed);
        ||角度取模,防止超出范围
        ptItem->fAngle = fmodf(ptItem->fAngle,ARM_2D_ANGLE(360));            
      }
        
        if (255 == ptItem->chOpacity) {
            //旋转不加透明度        
            arm_2dp_rgb565_tile_rotation((arm_2d_op_rotate_t *)&(ptItem->tOP),
                ptItem->ptTile,     //!< source tile
                ptTile,             //!< target tile
                &(ptItem->tRegion), //!< target region
                ptItem->tCentre,    //!< center point
                ptItem->fAngle,     //!< rotation angle
                GLCD_COLOR_BLACK,   //!< masking colour
                ptItem->ptTargetCentre);
        } else {
            //旋转加透明度 
            arm_2dp_rgb565_tile_rotation_with_alpha(
                &(ptItem->tOP),
                ptItem->ptTile,     //!< source tile
                ptTile,             //!< target tile
                &(ptItem->tRegion), //!< target region
                ptItem->tCentre,    //!< center point
                ptItem->fAngle,     //!< rotation angle
                GLCD_COLOR_BLACK,//GLCD_COLOR_BLACK,   //!< masking colour
                ptItem->chOpacity,  //!< Opacity
                ptItem->ptTargetCentre);
        }
    }
}
  • arm_foreach()为封装了for循环的宏函数。
  • ARM_2D_ANGLE()为角度转弧度的宏函数
  • 到这里我们就制作好了一个智能手表的显示界面,是不是感觉用Arm-2D很简单。

如果你觉得还不够美观,还可以做一个圆形的表盘,用半透明的颜色填充(比如50%的白色),程序如下:

arm_2d_region_t tBox = {.tLocation = {23,30},.tSize = {200, 200},};      
       
arm_2d_rgb565_fill_colour_with_mask_and_opacity(  
    ptTile, 
    &tBox, 
    &c_tileCircleMask, 
    (arm_2d_color_rgb565_t){GLCD_COLOR_WHITE},
    128 );    

效果如下图
image.png
素材制作也可以参考这篇文章,如下
【例说Arm-2D界面设计】做剪影风也太简单了

备注:本程序参考官方demo修改而来,感兴趣的也可以看看官方提供的demo例程,地址如下:

官方Arm-2D开源地址如下:
https://github.com/ARM-software/EndpointAI
参考demo《examples[pico]watch_panel[pfb]》

【说在后面的话】

表针的旋转在仪器仪表界面中还是很常用的,Arm-2D为我们提供的旋转功能还是非常实用的,相信大家通过上面的讲解已经会用Arm-2D的旋转功能了,使用时需要有几点注意,大家也已经Get到了,总结如下:

目标区域(target region)的作用是规定旋转后的图片贴到目标Tile的什么位置(超出部分会被裁剪掉

目标圆心(ptTargetCentre)为NULL时,默认就是目标区域的中心点,ptTargetCentre也可以指定为目标区域的任意一个坐标点

tCentre为source Tile的圆心,但不一定是source Tile的中心。一定要记得圆心不是中心。(* ̄︶ ̄)

好,在举一个仪器仪表界面中target region 为半圆的情况,如下图所示,
image.png

  • 显然我们的ptTargetCentre不能设置为NULL(默认值)

正确的设置如下图:
image.png

  • 看到了吧,有了这两个圆心(tCentre,ptTargetCentre)确定旋转中心还是很方便的。

下面我用Arm-2D发了个红包,点了串鞭炮给大家拜年了,视频演示如下:
image.png
补充

Arm-2D官方提供的一个小工具img2c.py

img2c.py-->将图片资源转化为Arm-2D可以使用的tile数据结构。

此Python程序在arm-2d\tools目录中有提供。

具体用法官方也在README.md中有说明。我简单说一下他的使用方法:

python img2c.py-i .\picture.png --dim 50 50 --name Circle

  • .\picture.png为图片路径
  • --dim 50 50这个参数是将图片大小缩放成50 * 50像素,可以不写,默认为图片的大小。
  • --name Circle为生成Tile变量的名字

img2c.py官方的说明我也贴到下面:
image.png

  • 图片格式可以是jpg、bmp、png等
  • --rot还可以设置图片的旋转角度
首发:裸机思维
作者:FledgingSu 支离苏

专栏推荐文章

如果你喜欢我的思维,欢迎订阅裸机思维
推荐阅读
关注数
1479
内容数
116
探讨嵌入式系统开发的相关思维、方法、技巧。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息