alpha像素混合可以在一个图层上面显示另外一张带透明的图片,以上一篇文章中的ICO图标解析为例.
//ICO解码所需的填充接口-会执行像素混合
void ICO_FillPoint_CallBack(GRAM_HANDLE* pHandle, u16 OffsetX, u16 OffsetY, void* pSourceImage, u16 SourceWidth, u16 SourceHeight)
{
static u32 offset;
static DMA2D_PixelMixing_Parm mMixingParm; //混合所需参数
offset = (u32)pHandle->Width * OffsetY + OffsetX;
offset *= pHandle->PixelByteSize;
//=====参数初始化======
//公共配置
mMixingParm.ImageWidth = SourceWidth; //待传输的像素图像宽度,单位像素
mMixingParm.ImageHeight = SourceHeight; //待传输的像素图像高度,单位像素
//输入的背景配置
mMixingParm.InBackImageAddr = pHandle->GRAM_Addr + offset; //输入的背景图像开始地址(通过地址控制开始的X,Y坐标)
mMixingParm.InBackImageOffsetX = pHandle->Width - SourceWidth; //输入的背景图像跳过的X坐标值
mMixingParm.InBackAlpha = 0xFF; //输入的背景图像的固定Alpha值,是否使用根据InBackAlphaMode配置决定
mMixingParm.InBackColorMode = pHandle->ColorMode; //输入的背景图像的颜色模式
mMixingParm.InBackAlphaMode = DMA2D_ALPHA_NULL; //输入的背景图像Alpha模式
//输入的前景配置
mMixingParm.InForeImageAddr = (u32)pSourceImage; //输入的前景图像开始地址(通过地址控制开始的X,Y坐标)
mMixingParm.InForeImageOffsetX = 0x00; //输入的前景图像跳过的X坐标值
mMixingParm.InForeAlpha = 0xFF; //输入的前景图像的固定Alpha值,是否使用根据InForeAlphaMode配置决定
mMixingParm.InForeColorMode = DMA2D_COLOR_ARGB8888; //输入的前景图像的颜色模式
mMixingParm.InForeAlphaMode = DMA2D_ALPHA_NULL; //输入的前景图像Alpha模式
//输出配置
mMixingParm.OutImageAddr = pHandle->GRAM_Addr + offset; //输出的图像开始地址(通过地址控制开始的X,Y坐标)
mMixingParm.OutImageOffsetX = pHandle->Width - SourceWidth; //输出的图像跳过的X坐标值
mMixingParm.OutColorMode = pHandle->ColorMode; //输出的图像的颜色模式
DMA2D_WaitTransferComplete(5); //需要等待上一次传输完成-不等待可能会出现乱点
DMA2D_FillImage_PixelMixing(&mMixingParm); //DMA2D进行矩形图形填充(会执行像素格式转换与Alpha混合)
}
上面这个是ICO解码的数据填充函数,输入的数据是ARGB格式,带透明信息,原本屏幕已经显示了一张底图,然后这个接口会自动对底图与新图进行Alpha混合,实现2个图片带透明度的叠加.
DMA2D_FillImage_PixelMixing()函数有硬件的,也提供一个对应的软件实现版本.
先上STM32F7的硬件DMA2D像素混合代码
/*************************************************************************************************************************
* 函数 : void DMA2D_WaitTransferComplete(u32 TimeOutMs)
* 功能 : 等待DMA2D传输完成
* 参数 : TimeOutMs:超时时间
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : [email protected]
* 时间 : 2019-10-30
* 最后修改时间 : 2019-10-30
* 说明 : 超时后会取消传输
2020-03-13:修改为通过传输启动状态来判断传输结束,由于传输结束,终止,出错后会自动清除启动位,如果没有启动,也不存在超时,比较灵活
*************************************************************************************************************************/
void DMA2D_WaitTransferComplete(u32 TimeOutMs)
{
u32 timeout = 0;
//while((DMA2D->ISR & (1<<1)) == 0) //等待传输完成
while(DMA2D->CR & BIT0) //等待传输结束
{
timeout++;
SYS_DelayMS(1); //延时1ms
if(timeout > TimeOutMs)
{
DMA2D_Abort(); //终止传输
break; //超时退出
}
}
DMA2D->IFCR |= 1<<1; //清除传输完成标志
}
/*************************************************************************************************************************
* 函数 : void DMA2D_FillImage_PixelMixing(DMA2D_PixelMixing_Parm *pParm)
* 功能 : DMA2D进行矩形图形填充(会执行像素格式转换与Alpha混合)
* 参数 : pParm:所需的参数,见DMA2D_PixelMixing_Parm
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : [email protected]
* 时间 : 2020-03-13
* 最后修改时间 : 2020-03-13
* 说明 : 用于填充图形,不会等待是否传输成功,需要调用DMA2D_WaitTransferComplete()等待传输结束
*************************************************************************************************************************/
void DMA2D_FillImage_PixelMixing(DMA2D_PixelMixing_Parm *pParm)
{
SYS_DeviceClockEnable(DEV_DMA2D, TRUE); //使能DMA2D时钟
DMA2D->CR = 1<<2; //配置寄存器先复位-停止传输
//传输模式设置
DMA2D->CR = DMA2D_MODE_MEM_MIXED<<16; //存储器到存储器模式-激活像素混合
//背景图像设置
DMA2D->BGPFCCR = ((u32)pParm->InBackAlpha&0XFF) << 24; //设置固定Alpha值
DMA2D->BGPFCCR |= (pParm->InBackAlphaMode & 0x03) << 16; //Alpha 模式 (Alpha mode)
DMA2D->BGPFCCR |= (pParm->InBackColorMode & 0x0f) << 0; //颜色模式
DMA2D->BGOR = pParm->InBackImageOffsetX; //背DD景层偏移寄存器
DMA2D->BGMAR = pParm->InBackImageAddr; //背景层存储器地址寄存器
//前景图像设置
DMA2D->FGPFCCR = ((u32)pParm->InForeAlpha&0XFF) << 24; //设置固定Alpha值
DMA2D->FGPFCCR |= (pParm->InForeAlphaMode & 0x03) << 16; //Alpha 模式 (Alpha mode)
DMA2D->FGPFCCR |= (pParm->InForeColorMode & 0x0f) << 0; //颜色模式
DMA2D->FGOR = pParm->InForeImageOffsetX; //前景层偏移寄存器
DMA2D->FGMAR = pParm->InForeImageAddr; //前景层存储器地址寄存器
//输出设置
DMA2D->OPFCCR = pParm->OutColorMode & 0x07; //输出颜色模式
DMA2D->OMAR = pParm->OutImageAddr; //输出存储器地址
DMA2D->OOR = pParm->OutImageOffsetX; //输出偏移寄存器
//公共配置
DMA2D->NLR = pParm->ImageWidth; //待传输的像素图像宽度,单位像素
DMA2D->NLR <<= 16;
DMA2D->NLR |= pParm->ImageHeight; //待传输的像素图像高度,单位像素
DMA2D_Start(); //开始传输
}
/*************************************************************************************************************
* 文件名 : dma2d.H
* 功能 : STM32F7 DMA2D驱动
* 作者 : [email protected]
* 创建时间 : 2019-10-29
* 最后修改时间 : 2019-10-29
* 详细:
*************************************************************************************************************/
#ifndef __DMA2D_H_
#define __DMA2D_H_
#include "system.h"
typedef enum
{
DMA2D_MODE_MEM = 0, //存储器到存储器(仅限 FG前景色 获取)
DMA2D_MODE_MEM_PFC = 1, //存储器到存储器并执行 PFC(仅限 FG PFC 激活时的 FG 获取)
DMA2D_MODE_MEM_MIXED= 2, //存储器到存储器并执行混合(执行 PFC 和混合时的 FG 和 BG 获取)
DMA2D_MODE_REG = 3, //寄存器到存储器(无 FG 和 BG,仅输出阶段激活)
}DMA2D_MODE;
//颜色模式
typedef enum
{
DMA2D_COLOR_ARGB8888 = 0,
DMA2D_COLOR_RGB888 = 1,
DMA2D_COLOR_RGB565 = 2,
/*DMA2D_COLOR_ARGB1555 = 3,
DMA2D_COLOR_ARGB4444 = 4,
DMA2D_COLOR_L8 = 5, //8 位 Luminance
DMA2D_COLOR_AL44 = 6, //4 位 Alpha,4 位 Luminance
DMA2D_COLOR_AL88 = 7, //8 位 Alpha,8 位 Luminance
DMA2D_COLOR_L4 = 8, //4 位 Luminance
DMA2D_COLOR_A8 = 9, //8 位 Alpha
DMA2D_COLOR_A4 = 10, //4 位 Alpha*/
}DMA2D_COLOR_MODE;
//Alpha 模式
typedef enum
{
DMA2D_ALPHA_NULL = 0,//不修改层图像的 alpha 通道值
DMA2D_ALPHA_REPLACE = 1,//原始层图像的 alpha 通道值替换为 ALPHA[7: 0]
DMA2D_ALPHA_PRODUCT = 2,//原始层图像的 alpha 通道值替换为 ALPHA[7: 0] 与原始 alpha 通道值的乘积
}DMA2D_ALPHA_MODE;
//像素混合模式下DMA图形填充所需参数
typedef struct
{
//公共配置
u16 ImageWidth; //待传输的像素图像宽度,单位像素
u16 ImageHeight; //待传输的像素图像高度,单位像素
//输入的背景配置
u32 InBackImageAddr; //输入的背景图像开始地址(通过地址控制开始的X,Y坐标)
u32 InBackImageOffsetX; //输入的背景图像跳过的X坐标值
u32 InBackAlpha; //输入的背景图像的固定Alpha值,是否使用根据InBackAlphaMode配置决定
DMA2D_COLOR_MODE InBackColorMode; //输入的背景图像的颜色模式
DMA2D_ALPHA_MODE InBackAlphaMode; //输入的背景图像Alpha模式
//输入的前景配置
u32 InForeImageAddr; //输入的前景图像开始地址(通过地址控制开始的X,Y坐标)
u32 InForeImageOffsetX; //输入的前景图像跳过的X坐标值
u32 InForeAlpha; //输入的前景图像的固定Alpha值,是否使用根据InForeAlphaMode配置决定
DMA2D_COLOR_MODE InForeColorMode; //输入的前景图像的颜色模式
DMA2D_ALPHA_MODE InForeAlphaMode; //输入的前景图像Alpha模式
//输出配置
u32 OutImageAddr; //输出的图像开始地址(通过地址控制开始的X,Y坐标)
u32 OutImageOffsetX; //输出的图像跳过的X坐标值
DMA2D_COLOR_MODE OutColorMode; //输出的图像的颜色模式
}DMA2D_PixelMixing_Parm;
__inline void DMA2D_Abort(void) {DMA2D->CR |= BIT2;} //终止传输
__inline void DMA2D_Suspend(void) {DMA2D->CR |= BIT1;} //挂起传输
__inline void DMA2D_Start(void) {DMA2D->CR |= BIT0;} //开始传输
void DMA2D_WaitTransferComplete(u32 TimeOutMs); //等待DMA2D传输完成
void DMA2D_FillImage_PixelMixing(DMA2D_PixelMixing_Parm *pParm); //DMA2D进行矩形图形填充(会执行像素格式转换与Alpha混合)
#endif //__DMA2D_H_
下面是C语言软件实现的alpha混合,功能与使用完全一致
//RGB888 转 RGB565
#ifndef RGB565
#define RGB565(color) ((((color) >> 19) & 0x1f) << 11) \
|((((color) >> 10) & 0x3f) << 5) \
|(((color) >> 3) & 0x1f)
#endif //RGB565
//RGB565转RGB888
#ifndef RGB888
#define RGB888(color) ((((color) >> 8) & 0xF8) << 16) \
|((((color) >> 3) & 0xFC) << 8) \
|(((color) << 3) & 0xFC)
#endif //RGB888
//RGB888转ARGB8888
#ifndef RGB888toARGB
#define RGB888toARGB(color) (color|0xFF000000)
#endif //RGB888toARGB
//ARGB 格式数据定义
typedef struct
{
u8 mB;
u8 mG;
u8 mR;
u8 mA;
}ARGB_DATA_TYPE;
//获取一个像素的数据长度
static u8 DMA2D_GetPidexByteSize(DMA2D_COLOR_MODE ColorMode)
{
switch (ColorMode)
{
case DMA2D_COLOR_ARGB8888: return 4;
case DMA2D_COLOR_RGB888: return 3;
default: return 2;
}
}
//转换一个像素为ARGB格式
static u32 __inline DMA2D_ConvPidexToARGB(u32 Color, DMA2D_COLOR_MODE ColorMode)
{
switch (ColorMode)
{
case DMA2D_COLOR_ARGB8888: //输入像素为ARGB8888,不用转换
{
return Color;
}
case DMA2D_COLOR_RGB888: //输入像素为RGB888,加上alpha通道数据
{
return RGB888toARGB(Color);
}
default: //输入像素为RGB565
{
return RGB888toARGB(RGB888(Color));
}
}
}
//处理一个像素的Alpha设置
static void __inline DMA2D_PidexAlphaModeHandle(u32 *pColor, u8 Alpha, DMA2D_ALPHA_MODE AlphaMode)
{
u16 temp;
ARGB_DATA_TYPE* pPidex = (ARGB_DATA_TYPE*)pColor;
switch (AlphaMode)
{
case DMA2D_ALPHA_REPLACE ://原始前景层图像的 alpha 通道值替换为 ALPHA[7: 0]
{
pPidex->mA = Alpha;
}break;
case DMA2D_ALPHA_PRODUCT ://原始前景层图像的 alpha 通道值替换为 ALPHA[7: 0] 与原始 alpha 通道值的乘积
{
temp = Alpha;
temp *= pPidex->mA;
pPidex->mA = temp / 255;
}break;
default: break;//不修改前景层图像的 alpha 通道值
}
}
//进行2个像素混合,必须是ARGB888格式
static void __inline DMA2D_PixelMixing(ARGB_DATA_TYPE* pInBackPidex, ARGB_DATA_TYPE* pInForePidex, ARGB_DATA_TYPE* pOutPidex)
{
ARGB_DATA_TYPE tempARGB;
//先计算底层(背景默认为黑色)-底层有个黑色幕布,如果背景层的Alpha不是255,会先进行计算
if (pInBackPidex->mA < 255)
{
tempARGB.mR = ((u32)(pInBackPidex->mA) * pInBackPidex->mR) / 255;
tempARGB.mG = ((u32)(pInBackPidex->mA) * pInBackPidex->mG) / 255;
tempARGB.mB = ((u32)(pInBackPidex->mA) * pInBackPidex->mB) / 255;
}
else
{
memcpy(&tempARGB, pInBackPidex, 4); //不用计算混合,直接使用背景层
}
//背景层与前景层混合
pOutPidex->mR = (((u32)(pInForePidex->mA) * pInForePidex->mR) + (u32)(255 - pInForePidex->mA) * tempARGB.mR) / 255;
pOutPidex->mG = (((u32)(pInForePidex->mA) * pInForePidex->mG) + (u32)(255 - pInForePidex->mA) * tempARGB.mG) / 255;
pOutPidex->mB = (((u32)(pInForePidex->mA) * pInForePidex->mB) + (u32)(255 - pInForePidex->mA) * tempARGB.mB) / 255;
pOutPidex->mA = 0xFF; //混合后的最终图像不需要Alpha了,因为屏幕显示只需要RGB即可
}
//将ARGB8888数据格式转换为输出所需格式
static u32 __inline DMA2D_ARGBtOConvPidex(u32 Color, DMA2D_COLOR_MODE ColorMode)
{
switch (ColorMode)
{
case DMA2D_COLOR_ARGB8888: //输出像素为ARGB8888,不用转换
{
return Color;
}
case DMA2D_COLOR_RGB888: //输出像素为RGB888,去掉alpha通道数据
{
return Color & 0x00FFFFFF;
}
default: //输出像素为RGB565
{
return RGB565(Color);
}
}
}
/*************************************************************************************************************************
* 函数 : void DMA2D_FillImage_PixelMixing(DMA2D_PixelMixing_Parm *pParm)
* 功能 : DMA2D进行矩形图形填充(会执行像素格式转换与Alpha混合)
* 参数 : pParm:所需的参数,见DMA2D_PixelMixing_Parm
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : [email protected]
* 时间 : 2020-03-14
* 最后修改时间 : 2020-03-14
* 说明 : 用于填充图形,实现STM32F7的 DMA2D像素混合功能
*************************************************************************************************************************/
void DMA2D_FillImage_PixelMixing(DMA2D_PixelMixing_Parm* pParm)
{
u32 x, y;
//像素点缓冲区
u32 InBackPidex; //输入的背景像素
u32 InForePidex; //输入的前景像素
u32 OutPidex; //输出的像素
//像素点指针-用于alpha混合
ARGB_DATA_TYPE* pInBackPidex; //输入的背景像素ARGB格式指针
ARGB_DATA_TYPE* pInForePidex; //输入的前景像素ARGB格式指针
ARGB_DATA_TYPE* pOutPidex; //输出的像素ARGB格式指针
//像素大小-单位字节
u8 InBackPidexSize; //输入的背景像素
u8 InForePidexSize; //输入的前景像素
u8 OutPidexSize; //输出的像素
//当前处理的像素位置指针-字节指针
u8* pInBackPidexBytePoint; //输入的背景像素处理位置指针
u8* pInForePidexBytePoint; //输入的前景像素处理位置指针
u8* pOutPidexBytePoint; //输出的像素字节指针
//像素指针初始化
pInBackPidex = (ARGB_DATA_TYPE*)&InBackPidex; //输入的背景像素
pInForePidex = (ARGB_DATA_TYPE*)&InForePidex; //输入的前景像素
pOutPidex = (ARGB_DATA_TYPE*)&OutPidex; //输出的像素
//像素大小初始化
InBackPidexSize = DMA2D_GetPidexByteSize(pParm->InBackColorMode); //输入的背景像素
InForePidexSize = DMA2D_GetPidexByteSize(pParm->InForeColorMode); //输入的前景像素
OutPidexSize = DMA2D_GetPidexByteSize(pParm->OutColorMode); //输出的像素
//像素处理位置指针初始化
pInBackPidexBytePoint = (u8*)pParm->InBackImageAddr; //输入的背景像素处理位置指针
pInForePidexBytePoint = (u8*)pParm->InForeImageAddr; //输入的前景像素处理位置指针
pOutPidexBytePoint = (u8*)pParm->OutImageAddr; //输出的像素字节指针
for (y = 0; y < pParm->ImageHeight; y++)
{
for (x = 0; x < pParm->ImageWidth; x++)
{
//像素数据初始化为0
InBackPidex = 0; InForePidex = 0; OutPidex = 0;
//读取一个背景像素,转换为ARGB格式
memcpy(&InBackPidex, pInBackPidexBytePoint, InBackPidexSize); //读取一个输入的背景像素
InBackPidex = DMA2D_ConvPidexToARGB(InBackPidex, pParm->InBackColorMode); //转换为ARGB
DMA2D_PidexAlphaModeHandle(&InBackPidex, pParm->InBackAlpha, pParm->InBackAlphaMode); //处理一个像素的Alpha设置
pInBackPidexBytePoint += InBackPidexSize; //指针跳到下一个像素
//读取一个前景像素,转换为ARGB格式
memcpy(&InForePidex, pInForePidexBytePoint, InForePidexSize); //读取一个输入的前景像素
InForePidex = DMA2D_ConvPidexToARGB(InForePidex, pParm->InForeColorMode); //转换为ARGB
DMA2D_PidexAlphaModeHandle(&InForePidex, pParm->InForeAlpha, pParm->InForeAlphaMode); //处理一个像素的Alpha设置
pInForePidexBytePoint += InForePidexSize; //指针跳到下一个像素
//Alpha混合
DMA2D_PixelMixing(pInBackPidex, pInForePidex, pOutPidex); //进行2个像素混合,必须是ARGB888格式
//输出像素转换
OutPidex = DMA2D_ARGBtOConvPidex(OutPidex, pParm->OutColorMode); //将ARGB8888数据格式转换为输出所需格式
//拷贝最终数据到输出缓冲区中
memcpy(pOutPidexBytePoint, &OutPidex, OutPidexSize);
pOutPidexBytePoint += OutPidexSize; //指针跳到下一个像素
}
//一行处理完了,需要跳过一些X值
pInBackPidexBytePoint += pParm->InBackImageOffsetX * InBackPidexSize;
pInForePidexBytePoint += pParm->InForeImageOffsetX * InForePidexSize;
pOutPidexBytePoint += pParm->OutImageOffsetX * OutPidexSize;
}
}
测试时,背景图片与后面的ICO透明图标都在一个图层上面显示
BMP_Show("C:\\system\\BL01.bmp", pLTDC_Layer1_GRAM_HANDLE, 0, 0, 0, 0, &pErrorStr);
ICO_Show("C:\\256x256.ico", pLTDC_Layer1_GRAM_HANDLE, 1, 1, 256, 256, &pErrorStr);
2个图片都是显示在LTDC_Layer1上,下面是开发板效果
这个是PC端,使用win32调用软件接口,效果与F7硬件DMA2D效果完全一致.