嵌入式系统与单片机|技术阅读
登录|注册

您现在的位置是:嵌入式系统与单片机 > 技术阅读 > 【玩转Arm-2D】七、制作酷炫圆环进度条

【玩转Arm-2D】七、制作酷炫圆环进度条

之前我们讲过一个矩形进度条的制作,忘记的可以看下面这篇文章


今天我们再讲一下怎么制作炫酷的圆环进度条,效果如下所示:

【制作前的准备知识】

一、子Tile

与矩形进度条一样,我们需要用到子Tile,之前我们讲过怎么用arm_2d_tile_generate_child函数生成一个子Tile,如下

arm_2d_tile_t c_tileChild;arm_2d_tile_generate_child( &Parenttile,//父tile  &rotate_region,//子tile的region &ChildTile,//子tile  false);//默认为false

今天我们补充一个用宏函数impl_child_tile来初始化一个子tile,如下:

staticconst arm_2d_tile_t c_tileChild = impl_child_tile( ParentTile,//父tile        0,//子tile的region.tLocation.iX 0,//子tile的region.tLocation.iY 50,//子tile的region.tSize.iWidth 50,//子tile的region.tSize.iHeight );

这个是不是很简单,只需要知道父Tile和子Tile的区域就可以了(* ̄︶ ̄)

二、旋转函数

之前我们也讲过Arm-2D的旋转功能,如下


使用了arm_2dp_tile_rotation和arm_2dp_tile_rotation_with_alpha两个函数

arm_2dp_tile_rotation( (arm_2d_op_trans_t *)&(ptItem->tOP), ptItem->ptTile, //!< source tile ptTile, //!< target tile ptItem->ptRegion, //!< target region ptItem->tCentre, //!< center point ptItem->fAngle, //!< rotation angle GLCD_COLOR_BLACK, //!< masking colour ptItem->ptTargetCentre);//!< Target center point arm_2dp_tile_rotation_with_alpha( (arm_2d_op_trans_opa_t *)&(ptItem->tOP), ptItem->ptTile, //!< source tile ptTile, //!< target tile ptItem->ptRegion, //!< target region ptItem->tCentre, //!< center point ptItem->fAngle, //!< rotation angle GLCD_COLOR_BLACK, //!< masking colour ptItem->chOpacity, //!< Opacity ptItem->ptTargetCentre);//!< Target center point

今天我们在补充两个旋转函数arm_2dp_tile_rotation_with_src_mask和arm_2dp_tile_rotation_with_src_mask_and_opacity,如下:

arm_2dp_tile_rotation_with_src_mask( (arm_2d_op_trans_msk_t *)&(ptItem->tOP),                    //!< control block ptItem->ptTile, //!< source tile ptItem->ptMask, //!< source mask ptTile, //!< target tile ptItem->ptRegion, //!< target region ptItem->tCentre, //!< pivot on source ptItem->fAngle , //!< rotation angle ptItem->ptTargetCentre //!< Target center point); arm_2dp_tile_rotation_with_src_mask_and_opacity( &(ptItem->tOP),//!< control block ptItem->ptTile, //!< source tile ptItem->ptMask, //!< source mask ptTile, //!< target tile ptItem->ptRegion, //!< target region ptItem->tCentre, //!< pivot on source ptItem->fAngle, //!< rotation angle ptItem->chOpacity, //!< opacity ptItem->ptTargetCentre //!< Target center point);
  • 从名字我们就知道比上面两个函数多了一个src_mask,也就是source mask,那这个source mask有什么用呢?              有了它,可以使我们的抗锯齿效果变得更好,是不是很心动,赶快动手试试吧。


三、切图

这个功能大家都不陌生吧,Arm-2D自带的。

上面的旋转函数都有一个target region,只要我们旋转超出这个区域,Arm-2D就会给我们把超出去的部分裁剪掉,如下图所示:


像这种tile copy和fill_colour函数,都有一个target region,如下

arm_2d_fill_colour_with_mask_and_opacity( ptTarget, /* target tile address*/   &pTregion, /* target region address*/   &ChildTile,/* alpha tile address */   (__arm_2d_color_t){Colour},/* colour */    OPACITY);  

只要超出target region,Arm-2D都会帮我们剪切掉,也就是说有target region的地方就有切图功能,如下图所示:

其实子Tile的功能就相当于是对父Tile的剪切了。

看到了吧,Arm-2D无处不在的切图功能是不是被你们忽略了(* ̄︶ ̄)

四、圆环素材的制作

其实这个也很简单,就是需要一个圆环的png图片,如下

不过这里需要强调一点,就是我们的圆环素材的像素边长要为奇数,比如像素为55*55的素材要比54*54的效果好一些,因为奇数个像素圆心为整数(方便旋转),下面我以世界上最小的像素圆环3*3举例来计算圆心,如下图所示


【圆环旋转的原理】

制作旋转的圆环进度条,其原理也很简单,就是用到了旋转和切图这两个功能。首先我们把圆环分成左右两个部分,如下图:

这样用Arm-2D子tile的切图功能很容易就制作出左右两个半圆环了,程序如下

||图片素材71*71像素的圆环extern const arm_2d_tile_t c_tilegreen71RGB565;
||圆环左半圈const arm_2d_tile_t c_tileBigGreenCircleLeftHalf = impl_child_tile( c_tilegreen71RGB565, /*parent tile*/ 0, /*iX*/ 0, /*iY*/ 36, /*iWidth*/ 71 /*iHeight*/ );||圆环右半圈 const arm_2d_tile_t c_tileBigGreenCircleRightHalf = impl_child_tile( c_tilegreen71RGB565, /*parent tile*/ 35, /*iX*/ 0, /*iY*/ 36, /*iWidth*/ 71 /*iHeight*/ );

当我们的旋转角度小于180度的时候,只需要旋转右半圆就可以了,把旋转区域设置成右半圆的矩形区域,旋转超出区域的圆环部分Arm-2D就会帮我们裁剪掉(这样旋转右半圈就不会影响到左半圈,同样的旋转左半圈也不会影响到右半圈),如下图所示,圆环旋转45度角:

程序也很简单,我们使用抗锯齿效果更好arm_2dp_tile_rotation_with_src_mask_and_opacity函数来进行旋转,如下所示:

if(ptItem->fAngle < ARM_2D_ANGLE(180)){ ||旋转右半圈 arm_2dp_tile_rotation_with_src_mask_and_opacity( &(ptItem->tOP), //!< control block ptItem->ptTile, //!< source tile ptItem->ptMask, //!< source mask ptTile, //!< target tile ptItem->ptRegion, //!< target region ptItem->tCentre, //!< pivot on source ptItem->fAngle, //!< rotation angle ptItem->chOpacity , //!< opacity ptItem->ptTargetCentre //!< Target center point ); ||copy左半圈的图片 arm_2d_rgb16_tile_copy_with_colour_masking( &c_tileBigGreenCircleLeftHalf , ptTile, &ptRegion, GLCD_COLOR_BLACK, ARM_2D_CP_MODE_COPY);}
  • 这里需要注意的是角度180需要转换成弧度,用ARM_2D_ANGLE宏就可以。

同样的道理,当旋转角度大于180度时,右半圈的圆环已经旋转到左半圈,此时把左半圈的贴图去掉就可以了(注意旋转区域设置成左半圈),这样一个旋转圆环的原理就讲完了,是不是很简单(* ̄︶ ̄)

【炫酷圆环进度条的制作】

制作之前我们要再讲一个知识点,用来制作颜色可变的圆环,这样就可以动态调整颜色了,是不是很酷。

下面我们就讲一下今天的主角arm_2dp_gray8_tile_rotation这个函数,他是对8位色图进行旋转的,我们待会用这个函数旋转我们的mask Tile,然后把旋转后的Tile保存到RAM中,这样我们就可以给旋转后的mask Tile填充颜色,从而实现改变圆环的颜色。先看下他的原型长什么样,如下

arm_2dp_gray8_tile_rotation( arm_2d_op_trans_t *ptOP, //!< control block arm_2d_tile_t *ptSource, //!< source tile  arm_2d_tile_t *ptTarget,  //!< target tile  arm_2d_region_t *ptRegion, //!< target region  arm_2d_location_t tCentre,  //!< pivot on source float fAngle, //!< rotation angle   uint_fast8_t chMskColour);  //!< masking colour 
  • 第1个参数ptOP为Arm-2D旋转需要的控制块

  • 第2个参数ptSource为源Tile(即要旋转的图片)

  • 第3个参数ptTarget为目标tile(即吧旋转后的图片放到哪)

  • 第4个参数ptRegion为目标区域

  • 第5个参数tCentre为source tile的圆心

  • 第6个参数fAngle为旋转角度

  • 第7个参数chMskColour为masking colour (即抠图功能)


上面我们讲了怎么用左右两个半圆环制作圆环进度条,那能不能用1/4圆环进行制作呢?

答案是肯定的,接下来我们就用旋转1/4圆环来制作圆环进度条,原理也是很简单,如下图所示:

这个旋转区域是0~90度的,其他区域也是同样的原理(也就是说我们只要旋转1/4圆环,其他区域贴图就可以)。好,现在就看看代码怎么实现。

首先,我们制作一个圆环素材的mask,素材大小为71*71,如下

extern const arm_2d_tile_t c_tilecolor71Mask;const uint8_t c_bmpcolor71GRAY8[71*71] = {/* -0- */0x00, 0x00, 0x00, 0x00, 0xa4, 0xac, 0xac,...}__attribute__((section("arm2d.tile.c_tilecolor71Mask")))const arm_2d_tile_t c_tilecolor71Mask = { .tRegion = { .tSize = { .iWidth = 71, .iHeight = 71, }, }, .tInfo = { .bIsRoot = true, .bHasEnforcedColour = true, .tColourInfo = { .chScheme = ARM_2D_COLOUR_8BIT, }, }, .pchBuffer = (uint8_t *)c_bmpcolor71GRAY8,};

接着用宏函数impl_fb定义一块RAM,用来存放旋转后的mask

impl_fb(s_tileSpinWheelMask2, 71, 71, arm_2d_color_gray8_t, .tInfo.tColourInfo.chScheme = ARM_2D_COLOUR_8BIT, .tInfo.bHasEnforcedColour = true, );

用mask填充一个背景圆环

arm_2d_fill_colour_with_mask_and_opacity( ptTarget, &__centre_region, &c_tile_myCIRCLE_MASK, BG_color,//(__arm_2d_color_t){GLCD_COLOR_LIGHT_GREY}, OPACITY);

然后用子Tile取圆环的右上角(1/4圆环),准备旋转

arm_2d_tile_t c_tileGreenCircleQuaterMask;rotate_region.tLocation.iX = CIRCLE_RADIUS-1;rotate_region.tLocation.iY = 0;rotate_region.tSize.iWidth = CIRCLE_RADIUS;rotate_region.tSize.iHeight = CIRCLE_RADIUS;arm_2d_tile_generate_child( &c_tile_myCIRCLE_MASK,//父tile &rotate_region,//子tile的region &c_tileGreenCircleQuaterMask,//子tile false);//默认为false

调用arm_2dp_gray8_tile_rotation函数把圆环右上角的mask旋转角度存放到RAM中,如下

memset(s_tileSpinWheelMask2.pchBuffer, 0, sizeof(s_tileSpinWheelMask2Buffer));arm_2d_tile_t tileMaskFB = s_tileSpinWheelMask2;static arm_2d_op_rotate_t s_tMaskRotateCB = {0};
const arm_2d_location_t c_tCentre = { .iX = 0, .iY = CIRCLE_RADIUS-1,}; tileMaskFB.tRegion.tSize.iWidth = CIRCLE_DIAMETER;tileMaskFB.tRegion.tSize.iHeight = CIRCLE_DIAMETER;
if (bIsNewFrame) { s_fAngle += ARM_2D_ANGLE(6.0f); s_fAngle = fmodf(s_fAngle,ARM_2D_ANGLE(360));}
arm_2dp_gray8_tile_rotation(&s_tMaskRotateCB, &c_tileGreenCircleQuaterMask, &tileMaskFB, NULL, c_tCentre, s_fAngle, 0x00);

然后把旋转后的mask用子Tile剪切后填充颜色

if(s_fAngle < ARM_2D_ANGLE(90)){ rotate_region.tLocation.iX = CIRCLE_RADIUS-1;      rotate_region.tLocation.iY = 0;   }else if(s_fAngle < ARM_2D_ANGLE(180))  {...}else if(s_fAngle < ARM_2D_ANGLE(270))  { ...}else{ ...}arm_2d_tile_generate_child(&tileMaskFB, &rotate_region, &ChildTile, false);arm_2d_fill_colour_with_mask_and_opacity( ptTarget, &rotate_region, &ChildTile, (__arm_2d_color_t){Colour}, OPACITY);
  • 注意:此时子Tile的区域要根据旋转角度(90、180、270、360四个区域)进行调整。

  • 然后在用arm_2d_fill_colour_with_mask_and_opacity函数填充颜色,此时我们把Colour设置成变量就可以修改颜色了(* ̄︶ ̄)


剩下的区域分别用子Tile填充就可以了,到这里可以改变颜色(其实就是改变填充颜色)的圆环进度条就做好了。

当然,为了使圆环更好看,我们也可以用一张彩色的图片作为背景圆环,如下图旋转320度的效果

小结


旋转图片(半圆环)
旋转mask(1/4圆环)
FLASH
71*71*2(16位色一个像素占两个字节)
71*71(mask相当于8位色图,一个像素占1个字节)
RAM
不需要
71*71(需要保存旋转后的mask)
圆环颜色
不能改变
可以改变

从表中我们可以看到,旋转mask的圆环虽然可以改变圆环颜色,但是也需要一个RAM空间,大家可以根据需求进行取舍。

备注:这个1/4旋转圆环程序参考了官方的代码,感兴趣的也可以去看看,代码地址如下:

https://github.com/ARM-software/Arm-2D/blob/main/examples/common/controls/spinning_wheel.c

从上面所讲的圆环进度条来看,都需要使用整个圆环的素材来制作,但能不能只使用半个圆环或者1/4圆环素材进行制作呢?

当然也是可以的,此时需要使用Arm-2D为我们提供的镜像拷贝函数,如下

enum __arm_2d_copy_mode_t { ARM_2D_CP_MODE_COPY , ARM_2D_CP_MODE_FILL , ARM_2D_CP_MODE_Y_MIRROR , ARM_2D_CP_MODE_X_MIRROR , ARM_2D_CP_MODE_XY_MIRROR ,};arm_2d_rgb16_tile_copy_with_colour_masking( c_tile_QuarterCIRCLE, //1/4圆环 ptTarget, &rotate_region, GLCD_COLOR_BLACK, ARM_2D_CP_MODE_X_MIRROR);//X镜像
  • 此函数的最后一个参数就可以设置镜像拷贝,可以设置的枚举值我也贴到了函数上面。

下面我就用1/2圆环素材简单实现了一个旋转圆环,程序贴到下面供大家参考:

void spinning_wheel_half_show( const arm_2d_tile_t *ptTarget, //!< target tile const arm_2d_tile_t *c_tile_myHalfCircle,//!< source tile    const arm_2d_region_t alignment_region, //!< target region float fAngle, //!< rotation angle     const char            CircleRadius, //!< circle radius    bool                  bIsNewFrame){ static arm_2d_op_trans_t s_tRotateCB = {0}; static float s_fAngle = 0.0f; const arm_2d_location_t c_tCentre = { .iX = 0, .iY = CircleRadius-1, }; const arm_2d_location_t c_tTargetCentre = { .iX = alignment_region.tLocation.iX + CircleRadius -1, .iY = alignment_region.tLocation.iY +CircleRadius-1, }; arm_2d_region_t rotate_region = alignment_region;        if (bIsNewFrame) {            s_fAngle = ARM_2D_ANGLE(fAngle); } rotate_region.tSize.iWidth = CircleRadius; if(s_fAngle < ARM_2D_ANGLE(180)){    //小于180,用X镜像贴左边的半圆环 arm_2d_rgb16_tile_copy_with_colour_masking( c_tile_myHalfCircle, ptTarget, &rotate_region, GLCD_COLOR_BLACK, ARM_2D_CP_MODE_X_MIRROR);    //旋转区域设置成右半圆   rotate_region.tLocation.iX += CircleRadius-1; } //旋转半圆环   arm_2dp_tile_rotation( &s_tRotateCB, c_tile_myHalfCircle, //!< source tile ptTarget, //!< target tile &rotate_region, //!< target region c_tCentre, //!< center point s_fAngle, //!< rotation angle          GLCD_COLOR_BLACK, //!< masking colour &c_tTargetCentre);//!< Target center point }

有了这个圆环旋转函数,我们使用起来也很简单,如下

/*__arm_2d_align_top_left(__region, __width, __height) { code body that can use __top_left_region}*/arm_2d_align_top_left(ptTile->tRegion, 69, 69) { spinning_wheel_half_show( ptTile, &c_tilehalfCircleRGB565, __top_left_region,       s_fAngle, 35,        bIsNewFrame);//           }
  • 注意:我们使用了Arm-2D左上角对齐的宏arm_2d_align_top_left,在给target region传参数的时候就可以使用__top_left_region了。

同样的,在使用1/4圆环制作时,最好用圆环的右上角,这样方便旋转,如下图所示:

我们只要旋转1/4圆环,其他用镜像拷贝就可以了,是不是很简单。

对了,镜像拷贝还有一个函数,需要讲一下,即arm_2d_rgb565_tile_copy_with_src_mask,如下所示

arm_2d_rgb565_tile_copy_with_src_mask(   SRC_ADDR,     /*source tile address */   SRC_MSK_ADDR, /*source mask address */   DES_ADDR,     /*target tile address */   REGION,       /*region address */        MODE)         /*copy mode */  
  • 这个函数比上面那个copy_with_colour_masking函数要多开销一个mask的FLASH,但是适合获得通用性,切换背景颗粒感也不会很强(即抗锯齿效果更好)。

  • 最后一个参数MODE就是设置镜像模式的。

【说在后面的话】

我们一直在使用mask,但是这个图片的mask怎么制作呢?其实Arm-2D也给我们提供了Python工具【img2c.py】,此文件在\RTE\Acceleration目录下,其使用方法也在【README.md】文件中有介绍。众所周知,Arm-2D的旋转是可以设置抗锯齿效果的,其设置方法也很简单,如下图所示:

有了mask加抗锯齿功能,在使用arm_2dp_tile_rotation_with_src_mask_and_opacity旋转函数和arm_2d_rgb565_tile_copy_with_src_mask镜像拷贝函数,制作通用性强,抗锯齿效果好的圆环进度条就非常方便了,大家赶快动手试试吧。

原创不易,如果你喜欢我的公众号、觉得我 文章对你有所启发,

请务必“点赞、收藏、转发”,这对我很重要,谢谢!

欢迎订阅    嵌入式小书虫