之前我们讲过一个矩形进度条的制作,忘记的可以看下面这篇文章
今天我们再讲一下怎么制作炫酷的圆环进度条,效果如下所示:
【制作前的准备知识】
一、子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,如下:
static
const 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_opaicty两个函数
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_opacity(
(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_keying(
&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的圆环虽然可以改变圆环颜色,但是也需要一个RAM空间,大家可以根据需求进行取舍。
备注:这个1/4旋转圆环程序参考了官方的代码,感兴趣的也可以去看看,代码地址如下:
| https://github.com/ARM-softwa... |
从上面所讲的圆环进度条来看,都需要使用整个圆环的素材来制作,但能不能只使用半个圆环或者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_keying(
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镜像拷贝函数,制作通用性强,抗锯齿效果好的圆环进度条就非常方便了,大家赶快动手试试吧。
原文:嵌入式小书虫
作者:FledgingSu 支离苏
专栏推荐文章
- 【喂到嘴边了的模块】超级嵌入式系统“性能/时间”工具箱
- 【喂到嘴边了的模块】关于我在MDK中部署LVGL只用了5分钟这件小事
- 【喂到嘴边了的模块】准备徒手撸GUI?用Arm-2D三分钟就够了
- 【为宏正名】99%的人从第一天学习C语言就自废的武功
如果你喜欢我的思维,欢迎订阅裸机思维欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。