数码相框的准备——显示并缩放bmp图片

一、写在前面
后续的文章将会在电子书的基础上实现一个数码相框。在讲解框架之前,先讲解如何在LCD上显示bmp图片
并支持缩放。另外,后面在讲完一个知识点后便贴出源码实现,加深理解。
对于在LCD显示bmp位图,分三步:
1)解析BMP文件,获得位图数据
2)缩放图片
3)合并图片
下面按这三步依序讲解。
二、BMP图片格式解析与源码实现
1)理解BMP图片格式
	对于理解BMP,只需了解它的组成 和 两个结构体 就足以编程了。
1.1) 位图文件三部分组成,如下图

位图文件三部分组成

文件信息头:用来描述位图文件有关信息,如位图水平/垂直分辨率、位图文件名字、大小等
位图信息头:用来描述位图有关信息,如位图bpp,位图宽度、高度等
RGB颜色阵列:注意:Windows下,RGB颜色阵列存储的格式其实BGR

1.2)位图的两个结构体(只注释编程用到的)
typedef struct tagBITMAPFILEHEADER { /* bmfh */
		unsigned short bfType;   		// 文件的类型,该值必需是0x4D42,也就是字符'BM'
		unsigned long  bfSize;            	// 该位图文件的大小,用字节为单位
		unsigned short bfReserved1;  		// 保留,必须设置为0
		unsigned short bfReserved2;  		// 保留,必须设置为0
		unsigned long  bfOffBits;		// 文件头开始到实际的图象数据之间的字节的偏移量
}__attribute__((packed)) BITMAPFILEHEADER;;

typedef struct tagBITMAPINFOHEADER { /* bmih */
		unsigned long  biSize;			// 说明BITMAPINFOHEADER结构所需要的字数。
		unsigned long  biWidth;			// 说明图象的宽度,以象素为单位
		unsigned long  biHeight;		// 说明图象的高度,以象素为单位
		unsigned short biPlanes;
		unsigned short biBitCount;     		// 说明比特数/象素,其值为1、4、8、16、24、或32
		unsigned long  biCompression;
		unsigned long  biSizeImage;
		unsigned long  biXPelsPerMeter;
		unsigned long  biYPelsPerMeter;
		unsigned long  biClrUsed;
		unsigned long  biClrImportant;
}__attribute__((packed)) BITMAPINFOHEADER;
备注:__attribute__((packed)) 用于结构体对齐,这适用于较老的编译器。
对于新的编译器,可用  
	#pragma pack(push) /* 将当前pack设置压栈保存 */
	#pragma pack(1)    /* 必须在结构体定义之前使用 */
	/* ... */
	#pragma pack(pop) /* 恢复先前的pack设置 */
详解可参考:https://www.cnblogs.com/CZM-/p/5398720.html

说明:以上解析来源自:https://blog.51cto.com/redwolf/229096 (推荐阅读)

2)编程解析BMP的注意点
	a. Windows下,RGB颜色阵列存储的格式其实BGR
	b. 位图数据从左下角开始存放,而LCD显示从左上角开始。写入FB和绘制位图要注意这一点。
	c.下面源码实现的位图的bpp是24位,因LCD的bpp可能是16/24/32位,在显示一行前需转换
	
3)源码实现
static int CovertOneLine(int iWidth, int iSrcBpp, int iDstBpp, unsigned char *pudSrcDatas, unsigned char *pudDstDatas)
{
    unsigned int dwRed;
    unsigned int dwGreen;
    unsigned int dwBlue;
    unsigned int dwColor;
    
    unsigned short *pwDstDatas16bpp = (unsigned short *)pudDstDatas;
    unsigned int   *pwDstDatas32bpp = (unsigned int *)pudDstDatas;
    int i;
    
    int pos = 0;
    if(iSrcBpp != 24)
    {
        return -1;
    }
    if(iDstBpp == 24)
    {
        memcpy(pudDstDatas, pudSrcDatas, iWidth*3);
    }
    else
    {
        for(i = 0; i < iWidth; ++i)
        {
            dwBlue = pudSrcDatas[pos++];
            dwGreen = pudSrcDatas[pos++];
            dwRed = pudSrcDatas[pos++];
            if(iDstBpp == 32)
            {
                dwColor = (dwRed << 16) | (dwGreen << 8) | dwBlue;
                *pwDstDatas32bpp = dwColor;
                pwDstDatas32bpp++;
            }
            else if(iDstBpp == 16)
            {
                /* 565 */
    		dwRed   = dwRed >> 3;
    		dwGreen = dwGreen >> 2;
    		dwBlue  = dwBlue >> 3;
    		dwColor = (dwRed << 11) | (dwGreen << 5) | (dwBlue);
    		*pwDstDatas16bpp = dwColor;
    		pwDstDatas16bpp++;
            }
        }
    }
}
static int GetBmpPixelDatasFrmBMP(unsigned char *aFileHead, PT_PixelDatas ptPixelDatas)
{
    /* 解析BMP文件,获得位图首地址 */
    BITMAPFILEHEADER *p_tBitmapHeader;
    BITMAPINFOHEADER *p_tBitmapInfoHeader;
    
    int iWidth;
    int iHeight;
    int iBMPBpp;
    int y;
    
    unsigned char *pucSrc;
    unsigned char *pucDest;
    int iLineWidthAlign;
    int iLineWidthReal; 
    
    p_tBitmapHeader = (BITMAPFILEHEADER *)aFileHead;
    p_tBitmapInfoHeader = (BITMAPINFOHEADER *)(aFileHead + sizeof(BITMAPFILEHEADER));
    
    iWidth = p_tBitmapInfoHeader->biWidth;
    iHeight = p_tBitmapInfoHeader->biHeight;
    iBMPBpp = p_tBitmapInfoHeader->biBitCount;
    
    if(iBMPBpp != 24)
    {
        DBG_PRINTF("iBMPBpp = %d\n", iBMPBpp);
  	DBG_PRINTF("sizeof(BITMAPFILEHEADER) = %d\n", sizeof(BITMAPFILEHEADER));
  	return -1;
    }
    
    ptPixelDatas->iWidth = iWidth;
    ptPixelDatas->iHeight = iHeight;
    ptPixelDatas->aucPixelDatas = malloc(iWidth * iHeight * ptPixelDatas->iBpp / 8);
    ptPixelDatas->iLineBytes = (iWidth * ptPixelDatas->iBpp / 8);
    if(NULL == ptPixelDatas->aucPixelDatas)
    {
        return -1;
    }
    iLineWidthReal = iWidth * iBMPBpp / 8;
    iLineWidthAlign = (iLineWidthReal + 3) & ~0x3;
    pucSrc = aFileHead + p_tBitmapHeader->bfOffBits;
    pucSrc = pucSrc + (iHeight -1) * iLineWidthAlign;
    pucDest = ptPixelDatas->aucPixelDatas;
    
    /* 将位图映射到LCD的FrameBuffer */
    for(y = 0; y < iHeight; ++y)
    {
        CovertOneLine(iWidth, iBMPBpp, ptPixelDatas->iBpp, pucSrc, pucDest);
        pucSrc  -= iLineWidthAlign;
        pucDest += ptPixelDatas->iLineBytes;
    }
    return 0;
}

static void FreeBmpPixelDatas(PT_PixelDatas ptBmpPixelData)
{
    free(ptBmpPixelData->aucPixelDatas);
}
三、图像缩放算法(近邻取样插值)与源码实现

1)近邻取样插值的缩放原理
近邻取样插值的缩放原理
根据前后缩放比列不变,即 Dx / DW = Sx / SW,。可算出
缩放后每个像素点的坐标位置,然后在把原图的RGB写入缩放后对应坐标位置即可。

2)源码实现

#include <pic_operation.h>
#include <string.h>
#include <stdlib.h>

int PicZoom(PT_PixelDatas ptOriginPic, PT_PixelDatas ptZoomPic)
{
    unsigned long x;
    unsigned long y;
    unsigned long srcy;
    
    unsigned long dst_width = ptZoomPic->iWidth;
    unsigned long* SrcX_Table = malloc(sizeof(unsigned long) * dst_width);
    unsigned char *pucDest;
    unsigned char *pucSrc;
    unsigned long dwPixelBytes = ptOriginPic->iBpp/8;
    
    if(ptOriginPic->iBpp != ptZoomPic->iBpp)
    {
         return -1;
    }
    
    for (x =0; x < dst_width; ++x)//生成表 SrcX_Table,记录一行中每个像素缩放后的坐标位置
    {				  // 因为每行的每个像素缩放比例是一定的,可提前算出,加速
        SrcX_Table[x] = (x * ptOriginPic->iWidth/ ptZoomPic->iWidth);
    }
    
    for (y = 0; y < ptZoomPic->iHeight; ++y) // 处理一列
    {
        srcy = (y * ptOriginPic->iHeight / ptZoomPic->iHeight);
        pucDest = ptZoomPic->aucPixelDatas + y * ptZoomPic->iLineBytes;
        pucSrc = ptOriginPic->aucPixelDatas + srcy * ptOriginPic->iLineBytes;
        for (x=0;x<dst_width;++x)   // 处理一行,逐像素处理
        {
            memcpy(pucDest+x*dwPixelBytes, pucSrc+SrcX_Table[x]*dwPixelBytes, dwPixelBytes);
        }
    }
    
    free(SrcX_Table);
    return 0;
}
四、图片合并与源码实现
1)图片合并解释,看下图(有个错字“钟”-->中)

图片合并的解释
2)源码实现

int PicMerge(int iX, int iY, PT_PixelDatas ptSmallPic, PT_PixelDatas ptBigPic)
{
    int i;
    unsigned char *pucSrc;
    unsigned char *pucDst; 
    
    if ((ptSmallPic->iWidth > ptBigPic->iWidth)  ||
       (ptSmallPic->iHeight > ptBigPic->iHeight) ||
       (ptSmallPic->iBpp != ptBigPic->iBpp))
    {
  	return -1;
    }
    
    pucSrc = ptSmallPic->aucPixelDatas;
    pucDst = ptBigPic->aucPixelDatas + iY * ptBigPic->iLineBytes + iX * ptBigPic->iBpp / 8;
    
    for(i = 0; i < ptSmallPic->iHeight; ++i)
    {
        memcpy(pucDst, pucSrc, ptSmallPic->iLineBytes);
        pucSrc += ptSmallPic->iLineBytes;
        pucDst += ptBigPic->iLineBytes;
    }
    return 0;
}
五、mian.c 与 实验效果

main.c 测试源码如下:

/* 省略一堆头文件 */
/* ./show_file [-s Size] [-f freetype_font_file] [-h HZK] <text_file> */
int main(int argc, char ** argv)
{
	int iFdBmp;
    	int iRet;
    	unsigned char *pucBMPmem;
    	struct stat tBMPstat;
    	PT_DispOpr ptDispOpr;
    	extern T_PicFileParser g_tBmpFileParser;
    	
    	T_PixelDatas tPixelDatas;
    	T_PixelDatas tPixelDatasSmall;
    	T_PixelDatas tPixelDatasFB;
    	
    	if (argc != 2)
    	{
  		printf("%s <bmp_file>\n", argv[0]);
  		return -1;
    	}
    	
 	DebugInit();
 	InitDebugChanel();
 	DisplayInit();
 	ptDispOpr = GetDispOpr("fb");
 	ptDispOpr->DeviceInit();
 	ptDispOpr->CleanScreen(0);
    	
    	/* 打开BMP文件 */
 	iFdBmp = open(argv[1], O_RDWR);
 	if (iFdBmp == -1)
 	{
  		DBG_PRINTF("can't open %s\n", argv[1]);
 	}
 	fstat(iFdBmp, &tBMPstat);
	pucBMPmem = (unsigned char *)mmap(NULL , tBMPstat.st_size, PROT_READ | PROT_WRITE, 
					MAP_SHARED, iFdBmp, 0);
 	if (pucBMPmem == (unsigned char *)-1)
 	{
  		DBG_PRINTF("mmap error!\n");
  		return -1;
 	}
    	/* 提取BMP文件的RGB数据, 缩放, 在LCD上显示出来 */
    	iRet = g_tBmpFileParser.isSupport(pucBMPmem);
    	if(iRet == 0)
    	{
        	DBG_PRINTF("%s is not bmp file\n", argv[1]);
  		return -1;
    	}
    	tPixelDatas.iBpp = ptDispOpr->iBpp;
    	
    	iRet = g_tBmpFileParser.GetPixelDatas(pucBMPmem, &tPixelDatas);
    	if (iRet)
 	{
  		DBG_PRINTF("GetPixelDatas error!\n");
  		return -1;  
 	}
    	tPixelDatasFB.iWidth        = ptDispOpr->iXres;
 	tPixelDatasFB.iHeight       = ptDispOpr->iYres;
 	tPixelDatasFB.iBpp          = ptDispOpr->iBpp;
 	tPixelDatasFB.iLineBytes    = ptDispOpr->iXres * ptDispOpr->iBpp / 8; 
 	tPixelDatasFB.aucPixelDatas = ptDispOpr->pucDispMem;
    	
    	PicMerge(0, 0, &tPixelDatas, &tPixelDatasFB);
    	
    	tPixelDatasSmall.iWidth  = tPixelDatas.iWidth/2;
 	tPixelDatasSmall.iHeight = tPixelDatas.iHeight/2;
 	tPixelDatasSmall.iBpp    = tPixelDatas.iBpp;
 	tPixelDatasSmall.iLineBytes = tPixelDatasSmall.iWidth * tPixelDatasSmall.iBpp / 8;
 	tPixelDatasSmall.aucPixelDatas = malloc(tPixelDatasSmall.iLineBytes * tPixelDatasSmall.iHeight);
    	
    	PicZoom(&tPixelDatas, &tPixelDatasSmall);
 	PicMerge(128, 128, &tPixelDatasSmall, &tPixelDatasFB);
 	
 	/* 记得释放 */
    	g_tBmpFileParser.FreePixelDatas(&tPixelDatas);
    	munmap(pucBMPmem, tBMPstat.st_size);
    	return 0;
}

实验效果图如下:
bmp缩放效果1
bmp缩放效果2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值