概述
测试环境为STM32F7,支持2个图层,可以支持图层之间显示透明度,测试方法为底层显示背景图标,顶层显示需要显示透明的图标,如果在单个图层显示,需要自己实现alpha混合,混合需要读取之前的像素,与要写入的像素进行混合,混合方法也比较简单可以网上查,或者我的上一篇文章 https://blog.csdn.net/cp1300/article/details/104748720 里面有用到像素混合.
准备工作,由于ico图标种类很多,目前解析的是最简单的,ARGB位图格式bmp,默认解析ico中第一个图标(ico中支持多个图标文件),可以使用 IconWorkshop这个软件将透明的png转换为ico,需要选择RGBA格式,并且不要压缩.
//解析代码如下
/*************************************************************************************************************
* 文件名 : IcoDecode.c
* 功能 : ICO图片软件解码
* 作者 : cp1300@139.com
* 创建时间 : 2020-03-11
* 最后修改时间 : 2020-03-11
* 详细 : 只支持32bit ARGB非压缩的ICO图片解析,ICO内部与bmp类似,从左下角开始刷新
*************************************************************************************************************/
#include "system.h"
#include "IcoDecode.h"
#include "PicDecode.h"
//检查是否需要分段加载文件(当开启了分段加载,并且当前解析位置到达了已经读取的文件结尾或超出了,则重新加载一包数据)
#define CheckisStageLoad() ((pDecodeTempData->isStageLoadFile) && (pDecodeTempData->FileOffset >= (pDecodeTempData->ThisReadCount + pDecodeTempData->ThisReadStartOffset)))
//一个ICO文件中的某一个图标信息体
#ifdef WIN32
#pragma pack (1) //强制使用字节对齐
typedef struct //__packed:让结构体的各个元素紧挨着存储
#else //MDK
typedef __packed struct
#endif //WIM32
{
u8 bWidth; //图像宽度,单位:像素
u8 bHeight; //图像高度,单位:像素
u8 bColorCount; //颜色数
u8 bReserved; //保留,为0
u16 wPlanes; //平面数,一般为1
u16 wBitCount; //每像素比特数
u32 dwBytesInRes; //数据块大小
u32 dwImageOffset;//数据块偏移量
}ICO_ONE_INFO;
#ifdef WIN32
#pragma pack() //取消自定义字节对齐方式。
#endif //WIN32
//一个ICO文件中的某一个图标信息体
#ifdef WIN32
#pragma pack (1) //强制使用字节对齐
typedef struct //__packed:让结构体的各个元素紧挨着存储
#else //MDK
typedef __packed struct
#endif //WIM32
{
u16 idReserved; //保留,为0
u16 idType; //文件类型,图标为1,光标为2
u16 idCount; //图象个数
ICO_ONE_INFO IcoInfoBuff[1];
}ICO_FILE_HEADER;
#ifdef WIN32
#pragma pack() //取消自定义字节对齐方式。
#endif //WIN32
//32位BMP文件头部信息结构-ico图标内也有bmp头
#ifdef WIN32
#pragma pack (1) //强制使用字节对齐
typedef struct //__packed:让结构体的各个元素紧挨着存储
#else //MDK
typedef __packed struct
#endif //WIM32
{
u32 bmfHeaderSize; //图像描述信息块的大小
int biWidth; //说明图象的宽度,以象素为单位
int biHeight; //说明图象的高度,以象素为单位(XOR图高度+AND图高度)
u16 biPlanes; //为目标设备说明位面数,其值将总是被设为1
u16 biBitCount; //说明比特数/象素,其值为1、4、8、16、24、或32,目前只支持32bit
u32 biCompression; //说明图象数据压缩的类型。必须为0
u32 biSizeImage; //说明图象的大小,以字节为单位
u32 bfReserved1[4]; //保留
}ICO_BMP_HEADER;
#ifdef WIN32
#pragma pack() //取消自定义字节对齐方式。
#endif //WIN32
//ICO图片解析所需的变量数据集合
typedef struct
{
ICO_INFO mIcoInfo; //图片信息
ICO_Decode_Parm* pDecodeParm; //图片解析输入的参数
u32 OneReadCount; //一次读取文件大小限制
u32 ThisReadStartOffset; //当前一次读取的文件开始偏移(相对于整个文件的偏移)
u32 ThisReadCount;
u32* InData32bit; //临时指针,用于读取32BIT的图片颜色值-指向的是原始图片缓冲区
u32 FileOffset; //文件偏移,用于判断是否超出文件范围了
u16 xSaveValid; //x轴方向实际存储的像素数据字节数
u16 IcoY; //Ico图片像素Y坐标-不是LCD的屏幕坐标,是当前显示的Ico位置坐标
u16 xPos, yPos; //屏幕坐标-屏幕画点坐标
float ScaleFactor; //图片收缩系数,1:不收缩;<1将缩小,缩小是会保证宽高比不变
float FactorCumulaX; //X方向缩放累加计数器,浮点型,每当增1后显示当前列
float FactorCumulaY; //Y方向缩放累加计数器,浮点型,每当增1后显示当前行
u16 LastFactorCumulaIntX; //X方向缩放累加计数器,整形数,用于记录上一次的值,用于对比是否发生了增量(+1),来自于FactorCumulaX的整数部分
u16 LastFactorCumulaIntY; //Y方向缩放累加计数器,整形数,用于记录上一次的值,用于对比是否发生了增量(+1),来自于FactorCumulaY的整数部分
u16 offsetX, offsetY; //用于图片居中显示时,X,Y的偏移
u16 CachePixelCount; //缓存的像素数量
u16 CachePixelSize; //缓存的像素缓冲区大小,像素大小
u16 FillPixelXpos; //填充图形的像素X起始坐标
u8 OutPixelByte; //输出像素字节数,根据输出的像素格式决定,可以是2,3,4,对应RGB565,RGB888,ARGB8888
bool isZoom; //是否需要缩放
bool isDisplayThisPixel; //是否显示当前像素,TRUE:显示,FALSE:不显示,用于跳过一些像素,达到缩小图片的目的
bool isStageLoadFile; //用于指示后面的图片解析函数,是否需要分段加载文件(2种情况下不需要,1:图片文件已经提前加载完成了,2:图片文件小于缓冲区大小,一次加载完成了)
}ICO_DecodeDataType;
static ICO_ERROR ICO_Decode32bit_Module(ICO_DecodeDataType* pDecodeTempData);//子流程-32bit ico位图解析(从内存中读取图片数据,然后解析到内存中)
/*************************************************************************************************************************
* 函数 : ICO_ERROR ICO_DecodeZoom_LoadFileData(ICO_DecodeDataType *pDecodeTempData)
* 功能 : 子流程-ICO解析:加载文件数据
* 参数 : pDecodeTempData:解析所需的变量;
* 返回 : ICO_ERROR
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2020-02-18
* 最后修改时间 : 2020-03-12
* 说明 : 请先调用CheckisStageLoad() 进行判断是否执行本函数
*************************************************************************************************************************/
ICO_ERROR ICO_DecodeZoom_LoadFileData(ICO_DecodeDataType* pDecodeTempData)
{
int ThisReadOffset; //记录当前读取时使用的偏移
if (pDecodeTempData->FileOffset > (pDecodeTempData->ThisReadCount + pDecodeTempData->ThisReadStartOffset))
{
ThisReadOffset = pDecodeTempData->FileOffset; //超出了文件范围,需要跳过一些字节,重新设置文件读偏移
}
else
{
ThisReadOffset = -1; //不需要偏移,接着之前的读取
}
pDecodeTempData->ThisReadStartOffset = pDecodeTempData->FileOffset; //记录新的读取位置偏移//pDecodeTempData->ThisReadCount; //当前数据偏移,加上上次读取的数据长度
//读取一包数据-接着读取,不需要偏移
if (pDecodeTempData->pDecodeParm->Func_ReadFile(pDecodeTempData->pDecodeParm->pInFileHandle, ThisReadOffset, pDecodeTempData->pDecodeParm->InFileBuff, pDecodeTempData->OneReadCount,
&pDecodeTempData->ThisReadCount) == FALSE)
{
DEBUG("读取ICO图片文件失败rn");
return ICO_FILE_ERROR;
}
//uart_printf("FileOffset:%d(%d)rn", pDecodeTempData->FileOffset, pDecodeTempData->ThisReadCount);
//读取一包数据,初始化相关信息
pDecodeTempData->InData32bit = (u32*)&pDecodeTempData->pDecodeParm->InFileBuff[0]; //跳到图片数据区-图片是分段加载的,起点就是正文处
return ICO_OK;
}
/*************************************************************************************************************************
* 函数 : ICO_ERROR ICO_DecodeZoom_NextValidRow(ICO_DecodeDataType* pDecodeTempData)
* 功能 : 子流程-ICO解析跳转到下一个能被显示的行(用于缩放)
* 参数 : pDecodeTempData:解析所需的变量;
* 返回 : ICO_ERROR
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2020-02-07
* 最后修改时间 : 2020-03-12
* 说明 : 无
*************************************************************************************************************************/
ICO_ERROR ICO_DecodeZoom_NextValidRow(ICO_DecodeDataType* pDecodeTempData)
{
u8* pData;
//需要缩放,那就先找到下一个能显示的行
while (pDecodeTempData->FileOffset < pDecodeTempData->pDecodeParm->InFileSize)
{
pDecodeTempData->FactorCumulaY += pDecodeTempData->ScaleFactor; //缩放系数累加,每当+1后就能被显示
if ((u16)pDecodeTempData->FactorCumulaY > pDecodeTempData->LastFactorCumulaIntY) //发生增1了
{
pDecodeTempData->LastFactorCumulaIntY = (u16)pDecodeTempData->FactorCumulaY; //记录
break; //退出循环
}
//跳过一行 32bit像素
pDecodeTempData->InData32bit += pDecodeTempData->mIcoInfo.biWidth; //文件指针自增,直接跳过一行
pDecodeTempData->FileOffset += 4 * pDecodeTempData->mIcoInfo.biWidth; //文件偏移
}
if (CheckisStageLoad()) //需要分段加载文件
{
ICO_DecodeZoom_LoadFileData(pDecodeTempData);//子流程-ICO解析:加载文件数据
}
return ICO_OK;
}
/*************************************************************************************************************************
* 函数 : ICO_ERROR ICO_Decode(ICO_Decode_Parm* pDecodeParm)
* 功能 : ICO图片软解析(全部解析)
* 参数 : pDecodeParm:相关参数(见ICO_Decode_Parm)
* 返回 : ICO_ERROR
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2011-09-19
* 最后修改时间 : 2020-02-19
* 说明 : 输入缓冲区最小要给128字节
*************************************************************************************************************************/
ICO_ERROR ICO_Decode(ICO_Decode_Parm* pDecodeParm)
{
ICO_FILE_HEADER* pIcoHeader; //图片文件头
ICO_DecodeDataType mDecodeTempData; //图片解析所需的临时变量
ICO_ERROR mIcoError = ICO_OK; //图片解析状态
u32 temp;
float ftemp;
u32 ActualReadCount;
ICO_BMP_HEADER* pICO_BmpHeader;
if (pDecodeParm == NULL || pDecodeParm->InFileBuff == NULL || pDecodeParm->InFileBuffSize < 128 || pDecodeParm->InFileSize == 0
|| ((pDecodeParm->isLoadFile == TRUE) && pDecodeParm->Func_ReadFile == NULL) || ((pDecodeParm->isLoadFile == TRUE) && pDecodeParm->pInFileHandle == NULL)
|| pDecodeParm->Func_FillPoint == NULL)
{
DEBUG("ICO解析失败:无效的参数(请提供正确的:pDecodeParm,缓冲区,缓冲区大小,文件大小,读取文件回调与文件句柄(前提是使能了文件加载),画点接口)rn");
return ICO_PARM_ERROR;
}
if (pDecodeParm->isLoadFile == TRUE) //需要加载文件
{
if (pDecodeParm->InFileBuffSize >= pDecodeParm->InFileSize) //缓冲区比文件大,可以一次加载完文件的所有数据
{
mDecodeTempData.ThisReadCount = pDecodeParm->InFileSize; //本次要读取的数据大小
mDecodeTempData.isStageLoadFile = FALSE; //图片文件一次加载完成了,不需要再加载了
}
else
{
mDecodeTempData.ThisReadCount = 128; //本次要读取的数据大小
mDecodeTempData.isStageLoadFile = TRUE; //需要分段加载文件
}
if (pDecodeParm->Func_ReadFile(pDecodeParm->pInFileHandle, 0, pDecodeParm->InFileBuff, mDecodeTempData.ThisReadCount, &ActualReadCount) == FALSE)
{
DEBUG("ICO解析失败:加载文件失败rn");
return ICO_FILE_ERROR; //文件加载失败
}
else if (mDecodeTempData.ThisReadCount != ActualReadCount)
{
DEBUG("ICO解析失败:文件加载不全rn");
return ICO_FILE_ERROR; //文件加载失败
}
}
else
{
mDecodeTempData.isStageLoadFile = FALSE; //图片文件已经提前加载了,不需要再加载了
}
//基本数据初始化
pIcoHeader = (ICO_FILE_HEADER*)pDecodeParm->InFileBuff; //得到ICO的头部信息
if (pIcoHeader->idType != 1)
{
DEBUG("ICO解析失败:不是有效的ICO图标文件rn");
return ICO_ILLEGAL_ERROR; //不支持
}
if (pIcoHeader->idCount == 0)
{
DEBUG("ICO解析失败:图标数量为0rn");
return ICO_ILLEGAL_ERROR; //不支持
}
if (pIcoHeader->IcoInfoBuff[0].wBitCount != 32)
{
DEBUG("ICO解析失败:只支持32bit的非压缩ARGB图标解析rn");
return ICO_ILLEGAL_ERROR; //不支持
}
mDecodeTempData.mIcoInfo.bfOffBits = pIcoHeader->IcoInfoBuff[0].dwImageOffset; //位图数据偏移地址偏移
pICO_BmpHeader = (ICO_BMP_HEADER*)&pDecodeParm->InFileBuff[mDecodeTempData.mIcoInfo.bfOffBits]; //获取bmp头信息
/****************************************************/
//调试
uart_printf("rn");
uart_printf("位图大小:%drn", pICO_BmpHeader->bmfHeaderSize);
uart_printf("颜色深度:%drn", pICO_BmpHeader->biBitCount);
uart_printf("水平分辨率:%drn", pICO_BmpHeader->biWidth);
uart_printf("垂直分辨率:%drn", pICO_BmpHeader->biHeight);
uart_printf("数据大小:%luBrn", pICO_BmpHeader->biSizeImage);
uart_printf("位图压缩:%drn", pICO_BmpHeader->biCompression);
/*****************************************************/
if (pICO_BmpHeader->bmfHeaderSize != 40 || pICO_BmpHeader->biCompression != 0 || pICO_BmpHeader->biBitCount != 32)
{
DEBUG("ICO解析失败:只支持32bit的非压缩ARGB图标解析(位图信息错误)rn");
return ICO_ILLEGAL_ERROR; //不支持
}
mDecodeTempData.mIcoInfo.biSizeImage = pICO_BmpHeader->biSizeImage; //图像数据大小
mDecodeTempData.mIcoInfo.biWidth = pICO_BmpHeader->biWidth; //图片宽度
mDecodeTempData.mIcoInfo.biHeight = pICO_BmpHeader->biHeight/2; //图片高度,位图的高度翻倍了
mDecodeTempData.mIcoInfo.biBitCount = pICO_BmpHeader->biBitCount; //颜色深度
mDecodeTempData.pDecodeParm = pDecodeParm; //记录输入的参数
mDecodeTempData.ThisReadCount = 0; //此次读取的文件大小复位
mDecodeTempData.ThisReadStartOffset = 0; //此次读取的文件开始偏移复位-相对于整个文件内的偏移
/****************************************************/
//调试
uart_printf("rn地址偏移:%drn", mDecodeTempData.mIcoInfo.bfOffBits);
uart_printf("颜色深度:%drn", mDecodeTempData.mIcoInfo.biBitCount);
uart_printf("水平分辨率:%drn", mDecodeTempData.mIcoInfo.biWidth);
uart_printf("垂直分辨率:%drn", mDecodeTempData.mIcoInfo.biHeight);
uart_printf("数据大小:%luBrn", mDecodeTempData.mIcoInfo.biSizeImage);
uart_printf("图像标志:0x%04Xrn", pIcoHeader->idType);
uart_printf("图形数量:%drnrn", pIcoHeader->idCount);
/*****************************************************/
//计算缩放比例
ftemp = pDecodeParm->OutRGB_ImageWidth;
ftemp /= (float)mDecodeTempData.mIcoInfo.biWidth; //水平缩放率
if (ftemp > 1) ftemp = 1; //只能缩小,不能放大
mDecodeTempData.ScaleFactor = pDecodeParm->OutRGB_ImageHeight;
mDecodeTempData.ScaleFactor /= (float)mDecodeTempData.mIcoInfo.biHeight; //垂直缩放率
if (mDecodeTempData.ScaleFactor > 1) mDecodeTempData.ScaleFactor = 1; //只能缩小,不能放大
if (ftemp < mDecodeTempData.ScaleFactor) //水平缩放比例大,会导致垂直方向无法填充满
{
mDecodeTempData.offsetX = 0;
mDecodeTempData.offsetY = (pDecodeParm->OutRGB_ImageHeight - (ftemp * mDecodeTempData.mIcoInfo.biHeight)) / 2; //计算居中坐标
mDecodeTempData.ScaleFactor = ftemp; //使用较小的那个缩放率进行缩放
}
else //垂直方向缩放比例大,会导致垂直方向无法填充满
{
mDecodeTempData.offsetY = 0;
mDecodeTempData.offsetX = (pDecodeParm->OutRGB_ImageWidth - (mDecodeTempData.ScaleFactor * mDecodeTempData.mIcoInfo.biWidth)) / 2; //计算居中坐标 //使用较小的那个缩放率进行缩放
}
uart_printf("图片缩放率:%frn", mDecodeTempData.ScaleFactor);
mDecodeTempData.FactorCumulaX = 0.0f; //X方向缩放累加计数器,浮点型,每当增1后显示当前列
mDecodeTempData.FactorCumulaY = 0.0f; //Y方向缩放累加计数器,浮点型,每当增1后显示当前行
mDecodeTempData.LastFactorCumulaIntX = 0; //X方向缩放累加计数器,整形数,用于记录上一次的值,用于对比是否发生了增量(+1)
mDecodeTempData.LastFactorCumulaIntY = 0; //Y方向缩放累加计数器,整形数,用于记录上一次的值,用于对比是否发生了增量(+1)
if (mDecodeTempData.ScaleFactor != 1.0f)
{
mDecodeTempData.isZoom = TRUE; //需要缩放
mDecodeTempData.isDisplayThisPixel = FALSE; //当前行列不能显示
}
else
{
mDecodeTempData.isZoom = FALSE; //不需要缩放
mDecodeTempData.isDisplayThisPixel = TRUE; //当前行列能显示
}
//初始化坐标-坐标显示范围由图片的真实宽度高度*缩放系数得到,ico从左下角开始解析
mDecodeTempData.IcoY = mDecodeTempData.mIcoInfo.biHeight * mDecodeTempData.ScaleFactor - 1; //ICO解析计数,变为0后解析完成
//输入指针初始化
mDecodeTempData.FileOffset = mDecodeTempData.mIcoInfo.bfOffBits + pICO_BmpHeader->bmfHeaderSize; //文件偏移(位图偏移加位图信息块大小)
if (mDecodeTempData.isStageLoadFile == FALSE) //不需要分段加载,直接解析内存中的数据
{
mDecodeTempData.InData32bit = (u32*)&pDecodeParm->InFileBuff[mDecodeTempData.FileOffset]; //跳到图片数据区
}
else //需要分包加载图片,计算一次读取的数据数量
{
mDecodeTempData.OneReadCount = pDecodeParm->InFileBuffSize / 4;
mDecodeTempData.OneReadCount *= 4;
mDecodeTempData.ThisReadStartOffset = mDecodeTempData.mIcoInfo.bfOffBits + pICO_BmpHeader->bmfHeaderSize; //文件偏移(位图偏移加位图信息块大小)
//读取第一包数据
if (pDecodeParm->Func_ReadFile(pDecodeParm->pInFileHandle, mDecodeTempData.ThisReadStartOffset+0, pDecodeParm->InFileBuff,
mDecodeTempData.OneReadCount, &mDecodeTempData.ThisReadCount) == FALSE)
{
mIcoError = ICO_FILE_ERROR;
DEBUG("读取ICO图片文件失败rn");
goto end_loop;
}
//读取的第一包数据,初始化相关信息
mDecodeTempData.InData32bit = (u32*)&pDecodeParm->InFileBuff[0]; //跳到图片数据区-图片是分段加载的,起点就是正文处
}
//输出像素
switch (pDecodeParm->OutRGB_ColorMode) //输出像素数据格式
{
case PIC_COLOR_ARGB8888: //32bit格式输出
{
mDecodeTempData.OutPixelByte = 4; //4字节
}break;
case PIC_COLOR_RGB888: //24bit格式输出
{
mDecodeTempData.OutPixelByte = 3; //3字节
}break;
default: //默认为RGB555
{
mDecodeTempData.OutPixelByte = 2; //2字节
}break;
}
//需要缩放,那就先找到下一个能显示的行
if (mDecodeTempData.isZoom) //需要缩放
{
mIcoError = ICO_DecodeZoom_NextValidRow(&mDecodeTempData); //子流程-ICO解析跳转到下一个能被显示的行(用于缩放)
if (mIcoError != ICO_OK)
{
goto end_loop;
}
}
//计算画点坐标初值, 让图片居中显示
mDecodeTempData.xPos = pDecodeParm->OutRGB_PixelOffsetX + mDecodeTempData.offsetX; //屏幕X坐标起始位置
//Y坐标偏移从左下角开始
mDecodeTempData.yPos = mDecodeTempData.IcoY + pDecodeParm->OutRGB_PixelOffsetY + mDecodeTempData.offsetY;
mDecodeTempData.CachePixelSize = pDecodeParm->DecodeBuffSize / mDecodeTempData.OutPixelByte; //计算像素缓冲区大小-像素大小
mDecodeTempData.CachePixelCount = 0; //缓存的像素计数器清零
mDecodeTempData.FillPixelXpos = mDecodeTempData.xPos; //填充图形开始坐标X值
//只支持32bit 非压缩ico解析
mIcoError = ICO_Decode32bit_Module(&mDecodeTempData); //子流程-32bit位图解析(从内存中读取图片数据,然后解析到内存中)
end_loop:
return mIcoError;
}
/*************************************************************************************************************************
* 函数 : void ICO_DecodeZoom_NextValidColumn(ICO_DecodeDataType *pDecodeTempData)
* 功能 : 子流程-ico位图解析跳转到下一个能被显示的列(用于缩放)
* 参数 : pDecodeTempData:解析所需的变量;
* 返回 : ICO_ERROR
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2020-02-07
* 最后修改时间 : 2020-03-12
* 说明 : 在外部判断 if(pDecodeTempData->isZoom == TRUE) 才调用此函数
*************************************************************************************************************************/
__inline void ICO_DecodeZoom_NextValidColumn(ICO_DecodeDataType* pDecodeTempData)
{
//if(pDecodeTempData->isZoom == TRUE) //需要缩放
{
pDecodeTempData->FactorCumulaX += pDecodeTempData->ScaleFactor; //缩放系数累加,每当+1后就能被显示
if ((u16)pDecodeTempData->FactorCumulaX > pDecodeTempData->LastFactorCumulaIntX) //发生增1了
{
pDecodeTempData->LastFactorCumulaIntX = (u16)pDecodeTempData->FactorCumulaX; //记录
pDecodeTempData->isDisplayThisPixel = TRUE; //需要显示
}
else
{
pDecodeTempData->isDisplayThisPixel = FALSE; //不需要显示
}
}
}
//RGB数据拷贝(系统自带的memcpy在某些对齐的情况下可能会导致程序崩溃)
static void ICO_memcpy(u8* destin, u8* source, u32 n)
{
while (n--)
{
*destin = *source;
destin++;
source++;
}
}
/*************************************************************************************************************************
* 函数 : static ICO_ERROR ICO_Decode32bit_Module(ICO_DecodeDataType *pDecodeTempData)
* 功能 : 子流程-32bit ico位图解析(从内存中读取图片数据,然后解析到内存中)
* 参数 : pDecodeTempData:解析所需的变量;
* 返回 : ICO_ERROR
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2020-03-12
* 最后修改时间 : 2020-03-12
* 说明 :
*************************************************************************************************************************/
static ICO_ERROR ICO_Decode32bit_Module(ICO_DecodeDataType* pDecodeTempData)
{
ICO_ERROR mIcoError = ICO_OK; //图片解析状态
u16 Xcnt = 0; //水平像素点计数(图片的实际水平分辨率计数)
u16 Colro_RGB565;
do
{
if (pDecodeTempData->isZoom == TRUE) ICO_DecodeZoom_NextValidColumn(pDecodeTempData); //子流程-ICO位图解析跳转到下一个能被显示的列(用于缩放)
if (pDecodeTempData->isDisplayThisPixel == TRUE) //需要显示
{
switch (pDecodeTempData->pDecodeParm->OutRGB_ColorMode) //输出像素数据格式
{
case PIC_COLOR_ARGB8888: //32bit格式输出-注意:ARGB格式,支持透明度
{
ICO_memcpy(&pDecodeTempData->pDecodeParm->DecodeBuff[pDecodeTempData->CachePixelCount * pDecodeTempData->OutPixelByte],
(u8 *)pDecodeTempData->InData32bit, pDecodeTempData->OutPixelByte);
//pDecodeTempData->pDecodeParm->Func_DrawPoint(pDecodeTempData->pDecodeParm->pGramHandle, pDecodeTempData->xPos, pDecodeTempData->yPos, *pDecodeTempData->InData32bit);
}break;
case PIC_COLOR_RGB888: //24bit格式输出,不支持透明度
{
ICO_memcpy(&pDecodeTempData->pDecodeParm->DecodeBuff[pDecodeTempData->CachePixelCount * pDecodeTempData->OutPixelByte],
(u8 *)pDecodeTempData->InData32bit, pDecodeTempData->OutPixelByte);
//ICO_memcpy((u8*)(&Color), (u8*)pDecodeTempData->InData32bit, 3);
//pDecodeTempData->pDecodeParm->Func_DrawPoint(pDecodeTempData->pDecodeParm->pGramHandle, pDecodeTempData->xPos, pDecodeTempData->yPos, Color);
}break;
default: //默认为RGB565,不支持透明度
{
Colro_RGB565 = RGB565(*pDecodeTempData->InData32bit);
ICO_memcpy(&pDecodeTempData->pDecodeParm->DecodeBuff[pDecodeTempData->CachePixelCount * pDecodeTempData->OutPixelByte],
(u8*)&Colro_RGB565, pDecodeTempData->OutPixelByte);
//pDecodeTempData->pDecodeParm->Func_DrawPoint(pDecodeTempData->pDecodeParm->pGramHandle, pDecodeTempData->xPos, pDecodeTempData->yPos, RGB565(*pDecodeTempData->InData32bit));
}break;
}
pDecodeTempData->xPos++; //画点水平位置增加
pDecodeTempData->CachePixelCount++; //缓冲的像素计数器增加
}
Xcnt++; //bmp图片水平像素计数器
pDecodeTempData->InData32bit++;
pDecodeTempData->FileOffset += 4; //文件偏移
if (pDecodeTempData->CachePixelCount == (pDecodeTempData->CachePixelSize - 1)) //需要批量刷新
{
pDecodeTempData->pDecodeParm->Func_FillPoint(pDecodeTempData->pDecodeParm->pGramHandle, pDecodeTempData->FillPixelXpos,
pDecodeTempData->yPos, pDecodeTempData->pDecodeParm->DecodeBuff, pDecodeTempData->CachePixelCount, 1);
pDecodeTempData->CachePixelCount = 0; //计数器清零
pDecodeTempData->FillPixelXpos = pDecodeTempData->xPos;
}
if (Xcnt == pDecodeTempData->mIcoInfo.biWidth) //换行
{
pDecodeTempData->xPos = pDecodeTempData->pDecodeParm->OutRGB_PixelOffsetX + pDecodeTempData->offsetX; //屏幕X坐标起始位置复位
//换行了也要重新刷新一下
if (pDecodeTempData->CachePixelCount > 0) //需要批量刷新
{
pDecodeTempData->pDecodeParm->Func_FillPoint(pDecodeTempData->pDecodeParm->pGramHandle, pDecodeTempData->FillPixelXpos,
pDecodeTempData->yPos, pDecodeTempData->pDecodeParm->DecodeBuff, pDecodeTempData->CachePixelCount, 1);
pDecodeTempData->CachePixelCount = 0; //计数器清零
}
pDecodeTempData->FillPixelXpos = pDecodeTempData->xPos;
Xcnt = 0; //水平计数器清零
//uart_printf("换行:%drn", pDecodeTempData->y);
pDecodeTempData->FactorCumulaX = 0; //X方向缩放累加计数器复位
pDecodeTempData->LastFactorCumulaIntX = 0;
if (pDecodeTempData->IcoY == 0) //结束了
{
uart_printf("图片解析结束rn");
break;
}
pDecodeTempData->yPos -= 1; //屏幕Y坐标更新
pDecodeTempData->IcoY--;
//需要缩放,找到下一个能被显示的y,这个是针对输入的文件行,不是输出的y
if (pDecodeTempData->isZoom == TRUE)
{
mIcoError = ICO_DecodeZoom_NextValidRow(pDecodeTempData); //子流程-bmp解析跳转到下一个能被显示的行(用于缩放)
if (mIcoError != ICO_OK)
{
break;
}
}
}
//2020-02-18 增加包加载文件支持
if (CheckisStageLoad()) //需要分段加载文件
{
ICO_DecodeZoom_LoadFileData(pDecodeTempData);//子流程-bmp解析:加载文件数据
}
} while (pDecodeTempData->FileOffset < pDecodeTempData->pDecodeParm->InFileSize);
return mIcoError;
}
/*************************************************************************************************************
* 文件名 : IcoDecode.h
* 功能 : ICO图片软件解码
* 作者 : cp1300@139.com
* 创建时间 : 2020-03-11
* 最后修改时间 : 2020-03-11
* 详细 : 只支持32bit ARGB非压缩的ICO图片解析
*************************************************************************************************************/
#ifndef __ICO_DECODE_H__
#define __ICO_DECODE_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include "system.h"
#include "PicDecode.h"
//软解码ICO状态
typedef enum
{
ICO_OK = 0, //解码成功
ICO_ILLEGAL_ERROR = 1, //非法的图片
ICO_COMP_ERROR = 2, //不支持压缩ico图片
ICO_PARM_ERROR = 3, //无效的参数
ICO_FILE_ERROR = 4, //文件读取失败,解析结束
}ICO_ERROR;
//软解码BMP图片相关的信息结构
typedef struct
{
u32 biSizeImage; //位图数据的大小
u16 bfOffBits ; //从文件开始到位图数据(bitmap data)开始之间的的偏移
u16 biWidth ; //图象的宽度,以象素为单位
u16 biHeight ; //图象的高度,以象素为单位
u8 biBitCount; //颜色深度
}ICO_INFO;
//解码ICO图片所需的参数(从存储器加载一部分,然后解析一部分)
typedef struct
{
//描述文件大小与文件缓冲区信息
u8 *InFileBuff; //输入的原始图片文件缓冲区,至少1KB
u32 InFileBuffSize; //输入的原始文件缓冲区大小(记录InFileBuff的大小)
u32 InFileSize; //输入的原始图片的总大小
//解码像素缓存-用于批量刷新到屏幕,提高效率,大小为4的倍数,如果为0,将不使用加速功能
u8* DecodeBuff; //解码缓冲区,需要外部初始化
u32 DecodeBuffSize; //解码缓冲区大小
u16 OutRGB_PixelOffsetX; //输出图片像素水平起始偏移-通常是相对于屏幕左上方偏移
u16 OutRGB_PixelOffsetY; //输出图片像素垂直起始偏移-通常是相对于屏幕左上方偏移
u16 OutRGB_ImageWidth; //输出图片宽度限制-一般是受屏幕显示范围限制(偏移+图片宽度 <= 屏幕宽度)
u16 OutRGB_ImageHeight; //输出图片高度限制-一般是受屏幕显示范围限制(偏移+图片高度 <= 屏幕高度)
PIC_COLOR_MODE OutRGB_ColorMode; //输出的RGB数据颜色模式,一定要与画点一致
//分布加载图片所需的文件句柄
void *pInFileHandle; //输入的原始文件句柄
bool isLoadFile; //是否需要加载文件,如果已经将所有的图片数据加载到InBmpFileBuff中,设置为FALSE,否则将会通过回调函数进行文件加载
//文件读取回调(返回:TRUE读取成功;FALSE:读取失败;),需要提前打开文件,并传输文件大小,文件句柄给解析函数,为了与文件系统进行解耦,不用关系文件位置
//文件读取回调 pFileHandle:文件句柄;Offset:文件偏移(-1:无需偏移,接着上次读取,文件系统一般都有此功能;其它>=0:文件起始偏移;
//pFileBuff:文件缓冲区;ReadCount:读取大小;pActualReadCount:返回实际读取的文件大小)
bool (*Func_ReadFile)(void *pFileHandle, int Offset,u8 *pFileBuff, u32 ReadCount, u32 *pActualReadCount);//文件读取回调
void* pGramHandle; //画点额外的GRAM句柄,可以根据 Func_DrawPoint()的需要为空,最终传递到 Func_DrawPoint()接口中
void (*Func_FillPoint)(void* pGramHandle, u16 OffsetX, u16 OffsetY, void* pSourceImage, u16 SourceWidth, u16 SourceHeight); //像素填充接口
}ICO_Decode_Parm;
ICO_ERROR ICO_Decode(ICO_Decode_Parm* pDecodeParm);//ICO图片软解析(全部解析)
#ifdef __cplusplus
}
#endif
#endif //__ICO_DECODE_H__
//输出图片颜色模式
typedef enum
{
PIC_COLOR_ARGB8888 = 0,
PIC_COLOR_RGB888 = 1,
PIC_COLOR_RGB565 = 2,
}PIC_COLOR_MODE;
//简单的封装
/*************************************************************************************************************************
* 函数 : bool ICO_Show(const char *pFilePath, GRAM_HANDLE *pGramHandle,u16 OffsetX,u16 OffsetY, u16 ImageWidth, u16 ImageHeight, const char **pErrorStr)
* 功能 : 显示一张ICO图片到GRAM(分包读取,节省内存)
* 参数 : pFilePath:文件路径;pGramHandle:GRAM句柄;OffsetX,OffsetY:显示开始在GRAM中的偏移;ImageWidth,ImageHeight:图片宽高限制;pErrorStr:错误字符串
* 返回 : TRUE:成功;FALSE:失败;
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2020-03-12
* 最后修改时间 : 2020-03-12
* 说明 : 从文件系统显示一张图片到GRAM
ImageWidth,ImageHeight:可以为0,将会自动填充满GRAM剩余位置
*************************************************************************************************************************/
bool ICO_Show(const char* pFilePath, GRAM_HANDLE* pGramHandle, u16 OffsetX, u16 OffsetY, u16 ImageWidth, u16 ImageHeight, const char** pErrorStr)
{
ICO_Decode_Parm mIcoDecodeParm; //软解码Ico图片所需的参数
u8* pFileBuff = NULL;
FILE_ERROR mFileError;
u32 FileSize;
FIL* pFile = NULL;
bool isStatus = TRUE;
u8* pDecodeBuff = NULL;
if (pFilePath == NULL || pGramHandle == NULL)
{
if (pErrorStr != NULL && *pErrorStr != NULL) *pErrorStr = "错误:无效的输入参数(路径或句柄).";
return FALSE;
}
//检查X坐标是否合法
if (OffsetX >= pGramHandle->Width)
{
if (pErrorStr != NULL && *pErrorStr != NULL) *pErrorStr = "错误:无效的GRAM宽度或X起始偏移.";
return FALSE;
}
if ((OffsetX + ImageWidth) > pGramHandle->Width || ImageWidth == 0) ImageWidth = pGramHandle->Width - OffsetX; //限制宽度,不要超出GRAM的限制
//检查Y坐标是否合法
if (OffsetY >= pGramHandle->Height)
{
if (pErrorStr != NULL && *pErrorStr != NULL) *pErrorStr = "错误:无效的GRAM高度或Y起始偏移.";
return FALSE;
}
if ((OffsetY + ImageHeight) > pGramHandle->Height || ImageHeight == 0) ImageHeight = pGramHandle->Height - OffsetY; //限制高度,不要超出GRAM的限制
//从文件系统加载图片
pFile = FILE_Open(pFilePath, FILE_READ, &mFileError); //打开文件
if (pFile == NULL) //打开失败
{
if (pErrorStr != NULL && *pErrorStr != NULL) *pErrorStr = "错误:打开文件失败.";
isStatus = FALSE;
goto close_file;
}
FileSize = FILE_GetSize(pFile);
if (FileSize > _BMP_IMAGE_MAX_SIZE || FileSize <= 50)
{
if (pErrorStr != NULL && *pErrorStr != NULL) *pErrorStr = "错误:文件大小不对.";
isStatus = FALSE;
goto close_file;
}
pFileBuff = mymalloc(SRAMEX, _IMAGE_CACHE_BUFF_SIZE); //申请内存
if (pFileBuff == NULL) //申请内存失败了
{
DEBUG("错误:内存不足.rn");
isStatus = FALSE;
goto close_file;
}
pDecodeBuff = mymalloc(SRAMEX, _IMAGE_DECODE_BUFF_SIZE); //申请内存
if (pDecodeBuff == NULL) //申请内存失败了
{
DEBUG("错误:内存不足.rn");
isStatus = FALSE;
goto close_file;
}
//文件打开成功,开始解析图片
//描述文件大小与文件缓冲区信息
mIcoDecodeParm.InFileBuff = pFileBuff; //输入的原始ICO图片文件缓冲区
mIcoDecodeParm.InFileSize = FileSize; //输入的原始ICO图片文件大小
mIcoDecodeParm.InFileBuffSize = _IMAGE_CACHE_BUFF_SIZE; //缓冲区大小
//解码像素缓存-用于批量刷新到屏幕,提高效率,大小为4的倍数,如果为0,将不使用加速功能
mIcoDecodeParm.DecodeBuff = pDecodeBuff; //解码缓冲区,需要外部初始化
mIcoDecodeParm.DecodeBuffSize = _IMAGE_DECODE_BUFF_SIZE; //解码缓冲区大小
mIcoDecodeParm.OutRGB_PixelOffsetX = OffsetX; //输出图片像素水平起始偏移-通常是相对于屏幕左上方偏移
mIcoDecodeParm.OutRGB_PixelOffsetY = OffsetY; //输出图片像素垂直起始偏移-通常是相对于屏幕左上方偏移
mIcoDecodeParm.OutRGB_ImageWidth = ImageWidth; //输出图片宽度限制-一般是受屏幕显示范围限制(偏移+图片宽度 <= 屏幕宽度)
mIcoDecodeParm.OutRGB_ImageHeight = ImageHeight; //输出图片高度限制-一般是受屏幕显示范围限制(偏移+图片高度 <= 屏幕高度)
mIcoDecodeParm.isLoadFile = TRUE; //需加载文件
mIcoDecodeParm.Func_ReadFile = BMP_ReadFile_CallBack; //文件读取回调函数
mIcoDecodeParm.pInFileHandle = pFile; //文件句柄
mIcoDecodeParm.pGramHandle = pGramHandle; //GRAM句柄
mIcoDecodeParm.Func_FillPoint = ICO_FillPoint_CallBack;
mIcoDecodeParm.OutRGB_ColorMode = (PIC_COLOR_MODE)pGramHandle->ColorMode; //输出的RGB数据颜色模式
if (ICO_Decode(&mIcoDecodeParm) == ICO_OK) //ICO图片解析
{
if (pErrorStr != NULL && *pErrorStr != NULL) *pErrorStr = "正常";
isStatus = TRUE;
}
close_file:
if (pFileBuff != NULL)
{
myfree(SRAMEX, pFileBuff); //释放掉内存
}
if (pDecodeBuff != NULL)
{
myfree(SRAMEX, pDecodeBuff); //释放掉内存
}
if (pFile != NULL)
{
FILE_Close(pFile, &mFileError); //关闭文件
}
return isStatus;
}
//数据填充接口-填充接口使用的DMA2D功能有硬件的也可以软件实现,会在下一篇文章中单独写.
//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混合)
}
//所需接口-文件读取接口
//文件读取回调
static bool BMP_ReadFile_CallBack(void *pFileHandle, int Offset,u8 *pFileBuff, u32 ReadCount, u32 *pActualReadCount)
{
FILE_ERROR mFileError;
if(Offset >= 0) //需要偏移
{
if(FILE_Lseek((FIL*)pFileHandle, Offset, &mFileError) == FALSE)
{
DEBUG("lseek错误:%drn", mFileError);
return FALSE;
}
}
if(FILE_Read((FIL*)pFileHandle, pFileBuff, ReadCount, pActualReadCount, &mFileError) == FALSE)
{
DEBUG("Read错误:%drn", mFileError);
return FALSE;
}
return TRUE;
}
如果硬件不支持Alphe混合,需要自己在画点函数中实现混合
//测试代码-请自己提前准备好测试图片
ICO_Show("C:\256x256.ico", pLTDC_Layer2_GRAM_HANDLE, 100, 30, 240, 240, &pErrorStr);
测试效果如下(中间的图标就是ico图标)
电脑上面的这个图标
最后
以上就是缥缈绿草为你收集整理的STM32 ICO图标解析(支持透明度)的全部内容,希望文章能够帮你解决STM32 ICO图标解析(支持透明度)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复