STM32 ICO图标解析(支持透明度)

测试环境为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图片文件失败\r\n");

        return ICO_FILE_ERROR;
    }
    //uart_printf("FileOffset:%d(%d)\r\n", 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,缓冲区,缓冲区大小,文件大小,读取文件回调与文件句柄(前提是使能了文件加载),画点接口)\r\n");
		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解析失败:加载文件失败\r\n");
			return ICO_FILE_ERROR;	//文件加载失败
		}
		else if (mDecodeTempData.ThisReadCount != ActualReadCount)
		{
			DEBUG("ICO解析失败:文件加载不全\r\n");
			return ICO_FILE_ERROR;	//文件加载失败
		}
	}
	else
	{
		mDecodeTempData.isStageLoadFile = FALSE;									//图片文件已经提前加载了,不需要再加载了
	}

	//基本数据初始化
	pIcoHeader = (ICO_FILE_HEADER*)pDecodeParm->InFileBuff;							//得到ICO的头部信息
	if (pIcoHeader->idType != 1)
	{
        DEBUG("ICO解析失败:不是有效的ICO图标文件\r\n");
        return ICO_ILLEGAL_ERROR;	//不支持
	}
	if (pIcoHeader->idCount == 0)
	{
        DEBUG("ICO解析失败:图标数量为0\r\n");
        return ICO_ILLEGAL_ERROR;	//不支持
	}
	if (pIcoHeader->IcoInfoBuff[0].wBitCount != 32)
	{
        DEBUG("ICO解析失败:只支持32bit的非压缩ARGB图标解析\r\n");
        return ICO_ILLEGAL_ERROR;	//不支持
	}

	mDecodeTempData.mIcoInfo.bfOffBits = pIcoHeader->IcoInfoBuff[0].dwImageOffset; 			//位图数据偏移地址偏移
    pICO_BmpHeader = (ICO_BMP_HEADER*)&pDecodeParm->InFileBuff[mDecodeTempData.mIcoInfo.bfOffBits]; //获取bmp头信息
    /****************************************************/
    //调试		
    uart_printf("\r\n");
    uart_printf("位图大小:%d\r\n", pICO_BmpHeader->bmfHeaderSize);
    uart_printf("颜色深度:%d\r\n", pICO_BmpHeader->biBitCount);
    uart_printf("水平分辨率:%d\r\n", pICO_BmpHeader->biWidth);
    uart_printf("垂直分辨率:%d\r\n", pICO_BmpHeader->biHeight);
    uart_printf("数据大小:%luB\r\n", pICO_BmpHeader->biSizeImage);
    uart_printf("位图压缩:%d\r\n", pICO_BmpHeader->biCompression);
    /*****************************************************/
    if (pICO_BmpHeader->bmfHeaderSize != 40 || pICO_BmpHeader->biCompression != 0 || pICO_BmpHeader->biBitCount != 32)
    {
        DEBUG("ICO解析失败:只支持32bit的非压缩ARGB图标解析(位图信息错误)\r\n");
        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("\r\n地址偏移:%d\r\n", mDecodeTempData.mIcoInfo.bfOffBits);
	uart_printf("颜色深度:%d\r\n", mDecodeTempData.mIcoInfo.biBitCount);
	uart_printf("水平分辨率:%d\r\n", mDecodeTempData.mIcoInfo.biWidth);
	uart_printf("垂直分辨率:%d\r\n", mDecodeTempData.mIcoInfo.biHeight);
	uart_printf("数据大小:%luB\r\n", mDecodeTempData.mIcoInfo.biSizeImage);
	uart_printf("图像标志:0x%04X\r\n", pIcoHeader->idType);
	uart_printf("图形数量:%d\r\n\r\n", 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("图片缩放率:%f\r\n", 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图片文件失败\r\n");
			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("换行:%d\r\n", pDecodeTempData->y);
            pDecodeTempData->FactorCumulaX = 0;									//X方向缩放累加计数器复位
            pDecodeTempData->LastFactorCumulaIntX = 0;
            if (pDecodeTempData->IcoY == 0) 									//结束了
            {
                uart_printf("图片解析结束\r\n");
                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("错误:内存不足.\r\n");
		isStatus = FALSE;
		goto close_file;
	}
	pDecodeBuff = mymalloc(SRAMEX, _IMAGE_DECODE_BUFF_SIZE);							//申请内存
	if (pDecodeBuff == NULL)															//申请内存失败了
	{
		DEBUG("错误:内存不足.\r\n");
		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错误:%d\r\n", mFileError);
			return FALSE;
		}
	}
	if(FILE_Read((FIL*)pFileHandle, pFileBuff, ReadCount, pActualReadCount, &mFileError) == FALSE)
	{
		DEBUG("Read错误:%d\r\n", mFileError);
		return FALSE;
	}
	
	return TRUE;
}

如果硬件不支持Alphe混合,需要自己在画点函数中实现混合

//测试代码-请自己提前准备好测试图片

ICO_Show("C:\\256x256.ico", pLTDC_Layer2_GRAM_HANDLE, 100, 30, 240, 240, &pErrorStr);

测试效果如下(中间的图标就是ico图标)

电脑上面的这个图标

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cp1300

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值