EPIC
HAL EPIC模块提供抽象的软件接口操作硬件EPIC模块,EPIC为一个2D图形引擎,它支持的特性
主要特性
alpha混叠两幅图片并保存到输出buffer
以任意点为中心旋转图片(前景图片), 将旋转后的图片与背景图片混叠,保存混叠后的结果到输出buffer
缩小/放到图片(前景图),将缩放后的图片与背景图混叠,保存混叠后的结果到输出buffer
一次GPU操作支持先旋转后缩放,不需要中间缓存buffer
在给定buffer中填充不透明或半透明色
所有的图形操作支持轮询和中断模式
输入和输出颜色格式不同即可实现颜色格式的自动转换
混叠的两幅图大小可以不同并部分重叠,还可以指定混叠后区域的一部分作为输出区域写入输出buffer
背景图和输出图可以复用同一个buffer, 比如背景图和输出图都使用frame buffer
输入图片格式支持EZIP
支持小数坐标混叠(55X 不支持)
输入输出限制
功能 |
55X |
58X |
56X |
54X |
---|---|---|---|---|
横向缩放 |
3.8, 即缩小8倍,放大256倍, 精度1/256 |
10.16, 即缩小1024倍,放大65536倍, 精度1/65536 |
同58X |
同58X |
纵向缩放 |
横向纵向缩放倍数固定一样,不能分开配置 |
10.16, 即缩小1024倍,放大65536倍, 精度1/65536, |
同58X |
同58X |
旋转角度 |
[0 ~ 3600], 单位为0.1度 |
同55X |
同55X |
同55X |
水平镜像 |
支持 |
支持 |
支持 |
支持 |
垂直镜像 |
不支持 |
不支持 |
不支持 |
支持 |
备注
旋转和缩放支持同时进行,并且支持同一锚点, 支持任意锚点。
镜像支持任意锚点,且不能和旋转、缩放同时进行。
前景、背景、输出区域的并集不超过1024*1024像素(其中前景指绕任意锚点变形后的图像区域(包括锚点和旋转前图片))
例如, 前景图为绕图片外锚点旋转并放大后, 与背景、输出区域的并集不超过1024
颜色格式支持
输入颜色格式支持 |
55X |
58X |
56X |
54X |
---|---|---|---|---|
RGB565/ARGB8565/RGB888/ARGB88888 |
Y |
Y |
Y |
Y |
L8 |
N |
Y |
Y |
Y |
A4/A8 (Mask,Overwrite,Fill) |
N |
Y |
Y |
Y |
YUV(YUYV/UYVY/iYUV) |
N |
N |
Y |
Y |
A2 (Fill) |
N |
N |
N |
Y |
输出颜色格式支持 |
55X |
58X |
56X |
54X |
---|---|---|---|---|
RGB565/ARGB8565/RGB888/ARGB88888 |
Y |
Y |
Y |
Y |
关图像问题的建议
用于旋转或缩放的图像在最外一圈加上一些透明像素(或者背景色),防止缩放时出现切边和旋转时出现锯齿
为防止缩放出现不连续,对于连续缩放场景缩放系数差值要大于1/256(即缩放精度不能超过1/256)
虽然旋转和缩放可以同时进行,但建议一次只执行一种变换以保证更好的输出图形质量
放大时建议使用图片左上角作为锚点,防止出现锚点抖动
EZIP格式的图片不能用于旋转
详细的API说明请参考EPIC 。
颜色在sram的存储格式
bit31~bit25 |
bit24~bit17 |
bit16~bit8 |
bit7~bit0 |
|
---|---|---|---|---|
RGB565 |
/ |
/ |
R4~R0G5~G3 |
G2~G0B4~B0 |
ARGB8565 |
/ |
A7 ~ A0 |
R4~R0G5~G3 |
G2~G0B4~B0 |
RGB888 |
/ |
R7 ~ R0 |
G7 ~ G0 |
B7 ~ B0 |
ARGB8888 |
A7 ~ A0 |
R7 ~ R0 |
G7 ~ G0 |
B7 ~ B0 |
A8 |
D7 ~ D0 |
C7~C0 |
B7~B0 |
A7~A0 |
A4 |
/ |
/ |
D3~D0C3~C0 |
B3~B0A3~A0 |
A2 |
/ |
/ |
H1H0G1G0F1F0E1E0 |
D1D0C1C0B1B0A1A0 |
备注
颜色数据均是紧密存放,在A2/A4/A8格式中ABCDEFGH代表像素点(从左到右显示)
使用HAL EPIC
首先调用 HAL_EPIC_Init()
初始化HAL EPIC. 在 EPIC_HandleTypeDef
结构中需指定EPIC实例(即使用的EPIC硬件模块),芯片只有一个EPIC实例 hwp_epic
。
初始化之后即可调用各种图形操作的接口处理数据。
例如,
static EPIC_HandleTypeDef epic_handle;
void init_epic(void)
{ // Initialize driver and enable EPIC IRQ
HAL_NVIC_SetPriority(EPIC_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(EPIC_IRQn);
epic_handle.Instance = hwp_epic;
HAL_EPIC_Init(&epic_handle);
}
/* EPIC IRQ Handler */
void EPIC_IRQHandler(void)
{
HAL_EPIC_IRQHandler(&epic_handle);
}
HAL_EPIC_Rotate_IT()
用于中断模式的混叠、旋转和缩放操作, HAL_EPIC_BlendStart_IT()
用于中断模式的混叠操作,需要在中断服务程序中调用 HAL_EPIC_IRQHandler()
处理中断。
@note HAL_EPIC_Rotate_IT()
实现了所有 HAL_EPIC_BlendStart_IT()
的功能,对于只是混叠的场景,建议使用 HAL_EPIC_BlendStart_IT()
,因为它的叠图吞吐率更高
混叠示例
如图1所示,示例blend_img_1
与blend_img_2
中,前景图所在区域的坐标为(10, 20)~(59,79),背景图所在区域的坐标为(0,0)~(99,99), 输出区域坐标为(5,10)~(44,59),所有坐标均为一个坐标系中的数值,体现三个区域的相对位置关系,
前景图以39%%的透明度与背景混叠,混叠后(5,10)~(44,59)区域的颜色值被顺序写入到输出buffer,其中与前景重叠的部分(画叉的部分,即区域[10,20]~[44,59])为混叠后的颜色,非重叠部分则是背景图中的颜色。
需要注意的是所有的数据buffer均为对应区域左上角像素的存放地址,例如fg_img.data指向前景图的坐标(10,20)所在像素的颜色值,outout_img.data指向输出区域的左上角像素,即(5,10)的颜色值。
背景图buffer与输出buffer不同
使用HAL_EPIC_Rotate_IT
void epic_cplt_callback(EPIC_HandleTypeDef *epic)
{
/* release the semaphore to indicate epic operation done */
sema_release(epic_sema);
}
/* blend the foreground with background image using 100 opacity (0 is transparent, 255 is opaque)
* output specified blended region to another buffer.
*
*/
void blend_img_1(void)
{
EPIC_BlendingDataType fg_img;
EPIC_BlendingDataType bg_img;
EPIC_BlendingDataType output_img;
EPIC_TransformCfgTypeDef trans_cfg;
HAL_StatusTypeDef ret;
/* foreground image, its coordinate (10,20)~(59,79) , buffer size is 50*60 */
HAL_EPIC_BlendDataInit(&fg_img);
fg_img.data = fg_img_buf;
fg_img.x_offset = 10;
fg_img.y_offset = 20;
/* blending area width */
fg_img.width = 50;
/* blending area height */
fg_img.height = 60;
/* image width, it can be different from fg_img.width */
fg_img.total_width = 50;
fg_img.color_mode = EPIC_COLOR_RGB565;
fg_img.color_en = false;
/* background image, its coordinate (0,0)~(99,99), buffer size is 100*100 */
HAL_EPIC_BlendDataInit(&bg_img);
bg_img.data = bg_img_buf;
bg_img.x_offset = 0;
bg_img.y_offset = 0;
bg_img.width = 100;
bg_img.height = 100;
bg_img.total_width = 100;
bg_img.color_mode = EPIC_COLOR_RGB565;
bg_img.color_en = false;
/* output image, its coordinate (5,10)~(44,59), buffer size is 40*50 */
HAL_EPIC_BlendDataInit(&output_img);
output_img.data = output_img_buf;
output_img.x_offset = 5;
output_img.y_offset = 10;
output_img.width = 40;
output_img.height = 50;
output_img.total_width = 40;
output_img.color_mode = EPIC_COLOR_RGB565;
output_img.color_en = false;
/* set complete callback */
epic_handle.XferCpltCallback = epic_cplt_callback;
/* no rotation and scaling, opacity 100
* start EPIC in interrupt mode
*/
HAL_EPIC_RotDataInit(&trans_cfg);
ret = HAL_EPIC_Rotate_IT(&epic_handle, &trans_cfg, &fg_img, &bg_img, &output_img, 100);
/* check ret value if any error happens */
...
/* wait for completion */
sema_take(epic_sema);
}
使用HAL_EPIC_BlendStart_IT
void epic_cplt_callback(EPIC_HandleTypeDef *epic)
{
/* release the semaphore to indicate epic operation done */
sema_release(epic_sema);
}
/* blend the foreground with background image using 100 opacity (0 is transparent, 255 is opaque)
* output specified blended region to another buffer.
*
*/
void blend_img_1(void)
{
EPIC_BlendingDataType fg_img;
EPIC_BlendingDataType bg_img;
EPIC_BlendingDataType output_img;
HAL_StatusTypeDef ret;
/* foreground image, its coordinate (10,20)~(59,79) , buffer size is 50*60 */
HAL_EPIC_BlendDataInit(&fg_img);
fg_img.data = fg_img_buf;
fg_img.x_offset = 10;
fg_img.y_offset = 20;
/* blending area width */
fg_img.width = 50;
/* blending area height */
fg_img.height = 60;
/* image width, it can be different from fg_img.width */
fg_img.total_width = 50;
fg_img.color_mode = EPIC_COLOR_RGB565;
fg_img.color_en = false;
/* background image, its coordinate (0,0)~(99,99), buffer size is 100*100 */
HAL_EPIC_BlendDataInit(&bg_img);
bg_img.data = bg_img_buf;
bg_img.x_offset = 0;
bg_img.y_offset = 0;
bg_img.width = 100;
bg_img.height = 100;
bg_img.total_width = 100;
bg_img.color_mode = EPIC_COLOR_RGB565;
bg_img.color_en = false;
/* output image, its coordinate (5,10)~(44,59), buffer size is 40*50 */
HAL_EPIC_BlendDataInit(&output_img);
output_img.data = output_img_buf;
output_img.x_offset = 5;
output_img.y_offset = 10;
output_img.width = 40;
output_img.height = 50;
output_img.total_width = 40;
output_img.color_mode = EPIC_COLOR_RGB565;
output_img.color_en = false;
/* set complete callback */
epic_handle.XferCpltCallback = epic_cplt_callback;
ret = HAL_EPIC_BlendStart_IT(&epic_handle, &fg_img, &bg_img, &output_img, 100);
/* check ret value if any error happens */
...
/* wait for completion */
sema_take(epic_sema);
}
背景图buffer与输出buffer相同
blend_img_2
展示了输出buffer复用背景图buffer的场景,这也是最常使用的,即frame buffer作为背景图buffer和输出buffer,
这种情况下背景图buffer中的(10,20)~(44,59)区域所在的颜色值会被修改为混叠后的颜色,其他位置的颜色值保持不变,
需要注意output_img.width和output_img.total_width的设置,output_img.width表示输出区域的宽度,即44-5+1=40,
但output_img.total_width表示输出buffer的宽度,因为输出buffer对应的图形大小为100*100,所以output_img.total_width应设为100,
这样EPIC在写完一行40个像素的数据后,会跳过余下的60个像素,继续更新下一行的数据。
fg_img和bg_img的width和total_width也是相同的含义。
使用HAL_EPIC_Rotate_IT
void epic_cplt_callback(EPIC_HandleTypeDef *epic)
{
/* release the semaphore to indicate epic operation done */
sema_release(epic_sema);
}
/* blend the foreground with background image using 100 opacity (0 is transparent, 255 is opaque)
* output buffer is same as background image buffer, usually they're both frame buffer.
*
*/
void blend_img_2(void)
{
EPIC_BlendingDataType fg_img;
EPIC_BlendingDataType bg_img;
EPIC_BlendingDataType output_img;
EPIC_TransformCfgTypeDef trans_cfg;
HAL_StatusTypeDef ret;
uint32_t buffer_start_offset;
/* foreground image, its coordinate (10,20)~(59,79), buffer size is 50*60 */
HAL_EPIC_BlendDataInit(&fg_img);
fg_img.data = fg_img_buf;
fg_img.x_offset = 10;
fg_img.y_offset = 20;
fg_img.width = 50;
fg_img.height = 60;
fg_img.total_width = 50;
fg_img.color_mode = EPIC_COLOR_RGB565;
fg_img.color_en = false;
/* background image, its coordinate (0,0)~(99,99), buffer size is 100*100 */
HAL_EPIC_BlendDataInit(&bg_img);
bg_img.data = bg_img_buf;
bg_img.x_offset = 0;
bg_img.y_offset = 0;
bg_img.width = 100;
bg_img.height = 100;
bg_img.total_width = 100;
bg_img.color_mode = EPIC_COLOR_RGB565;
bg_img.color_en = false;
/* output image, share the same buffer as bg_img_buf,
output area is (5,10)~(44,59), buffer size is 100*100 */
HAL_EPIC_BlendDataInit(&output_img);
/* topleft pixel is (5, 10), skip (10*100+5) pixels */
buffer_start_offset = (10 - 0) * 100 * 2 + (5 - 0) * 2;
output_img.data = (uint8_t *)((uint32_t)bg_img_buf + buffer_start_offset);
/* output area topleft coordinate */
output_img.x_offset = 5;
output_img.y_offset = 10;
/* output area width */
output_img.width = 40;
/* output area height */
output_img.height = 50;
/* output buffer width, it's different from output_img.width */
output_img.total_width = 100;
output_img.color_mode = EPIC_COLOR_RGB565;
output_img.color_en = false;
/* set complete callback */
epic_handle.XferCpltCallback = epic_cplt_callback;
/* no rotation and scaling, opacity 100
* start EPIC in interrupt mode
*/
HAL_EPIC_RotDataInit(&trans_cfg);
ret = HAL_EPIC_Rotate_IT(&epic_handle, &trans_cfg, &fg_img, &bg_img, &output_img, 100);
/* check ret value if any error happens */
...
/* wait for completion */
sema_take(epic_sema);
}
使用HAL_EPIC_BlendStart_IT
void epic_cplt_callback(EPIC_HandleTypeDef *epic)
{
/* release the semaphore to indicate epic operation done */
sema_release(epic_sema);
}
/* blend the foreground with background image using 100 opacity (0 is transparent, 255 is opaque)
* output buffer is same as background image buffer, usually they're both frame buffer.
*
*/
void blend_img_2(void)
{
EPIC_BlendingDataType fg_img;
EPIC_BlendingDataType bg_img;
EPIC_BlendingDataType output_img;
HAL_StatusTypeDef ret;
uint32_t buffer_start_offset;
/* foreground image, its coordinate (10,20)~(59,79), buffer size is 50*60 */
HAL_EPIC_BlendDataInit(&fg_img);
fg_img.data = fg_img_buf;
fg_img.x_offset = 10;
fg_img.y_offset = 20;
fg_img.width = 50;
fg_img.height = 60;
fg_img.total_width = 50;
fg_img.color_mode = EPIC_COLOR_RGB565;
fg_img.color_en = false;
/* background image, its coordinate (0,0)~(99,99), buffer size is 100*100 */
HAL_EPIC_BlendDataInit(&bg_img);
bg_img.data = bg_img_buf;
bg_img.x_offset = 0;
bg_img.y_offset = 0;
bg_img.width = 100;
bg_img.height = 100;
bg_img.total_width = 100;
bg_img.color_mode = EPIC_COLOR_RGB565;
bg_img.color_en = false;
/* output image, share the same buffer as bg_img_buf,
output area is (5,10)~(44,59), buffer size is 100*100 */
HAL_EPIC_BlendDataInit(&output_img);
/* topleft pixel is (5, 10), skip (10*100+5) pixels */
buffer_start_offset = (10 - 0) * 100 * 2 + (5 - 0) * 2;
output_img.data = (uint8_t *)((uint32_t)bg_img_buf + buffer_start_offset);
/* output area topleft coordinate */
output_img.x_offset = 5;
output_img.y_offset = 10;
/* output area width */
output_img.width = 40;
/* output area height */
output_img.height = 50;
/* output buffer width, it's different from output_img.width */
output_img.total_width = 100;
output_img.color_mode = EPIC_COLOR_RGB565;
output_img.color_en = false;
/* set complete callback */
epic_handle.XferCpltCallback = epic_cplt_callback;
ret = HAL_EPIC_BlendStart_IT(&epic_handle, &fg_img, &bg_img, &output_img, 100);
/* check ret value if any error happens */
...
/* wait for completion */
sema_take(epic_sema);
}
旋转示例
如图2所示,示例rotate_img将位于(10,20)~(59,79)的前景图以图中心为轴顺时针旋转30度,与背景图混叠后更新背景图对应位置的颜色,落在旋转后图形外的像素仍旧保持背景图的颜色。 由于旋转后图形覆盖的矩形区域会扩大(即[x0,y0]~[x1,y1]),为了保证旋转后的图形能被完整的显示出来,可以简单的将输出区域设成最大,HAL将自动计算旋转后的矩形区域, 当背景图buffer与输出buffer相同时, 只会更新输出buffer中被旋转区域覆盖的像素点的颜色。
/* rotate the foreground image by 30 degree (clockwisely) and blend it with background using 100 opacity (0 is transparent, 255 is opaque)
* output data is written back to background image buffer, it can also output to another buffer like blend_img_1.
*
*/
void rotate_img(void)
{
EPIC_BlendingDataType fg_img;
EPIC_BlendingDataType bg_img;
EPIC_BlendingDataType output_img;
EPIC_TransformCfgTypeDef trans_cfg;
HAL_StatusTypeDef ret;
/* foreground image, its coordinate (10,20)~(59,79) before rotation, buffer size is 50*60 */
HAL_EPIC_BlendDataInit(&fg_img);
fg_img.data = fg_img_buf;
fg_img.x_offset = 10;
fg_img.y_offset = 20;
fg_img.width = 50;
fg_img.height = 60;
fg_img.total_width = 50;
fg_img.color_mode = EPIC_COLOR_RGB565;
fg_img.color_en = false;
/* background image, its coordinate (0,0)~(99,99), buffer size is 100*100 */
HAL_EPIC_BlendDataInit(&bg_img);
bg_img.data = bg_img_buf;
bg_img.x_offset = 0;
bg_img.y_offset = 0;
bg_img.width = 100;
bg_img.height = 100;
bg_img.total_width = 100;
bg_img.color_mode = EPIC_COLOR_RGB565;
bg_img.color_en = false;
/* output image, its coordinate (0,0)~(99,99), share same buffer as background image */
HAL_EPIC_BlendDataInit(&output_img);
output_img.data = bg_img_buf;
output_img.x_offset = 0;
output_img.y_offset = 0;
output_img.width = 100;
output_img.height = 100;
output_img.total_width = 100;
output_img.color_mode = EPIC_COLOR_RGB565;
output_img.color_en = false;
epic_handle.XferCpltCallback = epic_cplt_callback;
/* foreground image is rotated by 30 degree around its center */
HAL_EPIC_RotDataInit(&trans_cfg);
trans_cfg.angle = 300;
trans_cfg.pivot_x = fg_img.width / 2;
trans_cfg.pivot_y = fg_img.height / 2;
trans_cfg.scale_x = 1000;
trans_cfg.scale_y = 1000;
/* no scaling, opacity 100
* start EPIC in interrupt mode
*/
ret = HAL_EPIC_Rotate_IT(&epic_handle, &trans_cfg, &fg_img, &bg_img, &output_img, 100);
/* check ret value if any error happens */
...
/* wait for completion */
sema_take(epic_sema);
}
缩放示例
如图3所示,示例scale_down_img将位于(10,20)~(59,79)的前景图横向与纵向都缩小到原图的71%%,同时保持图中心点位置不变。 类似旋转,也可以简单的将输出区域设成最大,如果输出buffer复用背景buffer,HAL将只更新缩小后区域(即[x0,y0]~[x1,y1])所包含的像素的颜色值。
/* scale down the foreground image by 1.4 and blend it with background using 100 opacity (0 is transparent, 255 is opaque)
* output data is written back to background image buffer, it can also output to another buffer like blend_img_1.
*
*/
void scale_down_img(void)
{
EPIC_BlendingDataType fg_img;
EPIC_BlendingDataType bg_img;
EPIC_BlendingDataType output_img;
EPIC_TransformCfgTypeDef trans_cfg;
HAL_StatusTypeDef ret;
/* foreground image, its coordinate (10,20)~(59,79) before scaling */
HAL_EPIC_BlendDataInit(&fg_img);
fg_img.data = fg_img_buf;
fg_img.x_offset = 10;
fg_img.y_offset = 20;
fg_img.width = 50;
fg_img.height = 60;
fg_img.total_width = 50;
fg_img.color_mode = EPIC_COLOR_RGB565;
fg_img.color_en = false;
/* background image, its coordinate (0,0)~(99,99) */
HAL_EPIC_BlendDataInit(&bg_img);
bg_img.data = bg_img_buf;
bg_img.x_offset = 0;
bg_img.y_offset = 0;
bg_img.width = 100;
bg_img.height = 100;
bg_img.total_width = 100;
bg_img.color_mode = EPIC_COLOR_RGB565;
bg_img.color_en = false;
/* output image, its coordinate (0,0)~(99,99), share same buffer as background image */
HAL_EPIC_BlendDataInit(&output_img);
output_img.data = bg_img_buf;
output_img.x_offset = 0;
output_img.y_offset = 0;
output_img.width = 100;
output_img.height = 100;
output_img.total_width = 100;
output_img.color_mode = EPIC_COLOR_RGB565;
output_img.color_en = false;
epic_handle.XferCpltCallback = epic_cplt_callback;
/* no rotation, both X and Y direction are scaled down by 1.4,
the image center is in the same position after scaling */
HAL_EPIC_RotDataInit(&trans_cfg);
trans_cfg.pivot_x = fg_img.width / 2;
trans_cfg.pivot_y = fg_img.height / 2;
trans_cfg.scale_x = 1400;
trans_cfg.scale_y = 1400;
/* opacity 100
* start EPIC in interrupt mode
*/
ret = HAL_EPIC_Rotate_IT(&epic_handle, &trans_cfg, &fg_img, &bg_img, &output_img, 100);
/* check ret value if any error happens */
...
/* wait for completion */
sema_take(epic_sema);
}
颜色填充示例
一个大小为100*90的buffer,在其(20,10)~(39, 49)区域填充颜色RGB(99,107,123),配置的颜色值为RGB888格式,填充后的颜色格式为RGB565,硬件会作颜色格式转换。 透明度为100,255表示不透明,0表示透明。 因为填充的第一个像素位置为(20,10),相对buffer的首地址有偏移,配置的起始地址应为偏移后的地址,total_width为buffer的总宽度,即100,width为填充区域的宽度,即(39-20+1)=20, 填充完一行20个像素的颜色后,会跳过余下的80个颜色,转到下一行继续填充,直到填完指定行数。
void fill_color(void)
{
EPIC_FillingCfgTypeDef param;
uint32_t start_offset;
HAL_StatusTypeDef ret;
HAL_EPIC_FillDataInit(¶m);
/* topleft pixel offset in the output buffer */
start_offset = 2 * (10 * 100 + 20);
param.start = (uint8_t *)((uint32_t)output_buf + start_offset);
/* filled color format RGB565 */
param.color_mode = EPIC_COLOR_RGB565;
/* filling area width */
param.width = 20;
/* filling area height */
param.height = 40;
/* filling buffer total width */
param.total_width = 100;
/* red part of RGB888 */
param.color_r = 99;
/* green part of RGB888 */
param.color_g = 107;
/* blue part of RGB888 */
param.color_b = 123;
/* opacity is 100 */
param.alpha = 100;
/* fill in polling mode */
ret = HAL_EPIC_FillStart(&epic_handle, ¶m);
/* check ret if any error happens */
}