注:本人已购买韦东山老师第三期项目视频,内容来源《数码相框项目视频》,只用于学习记录,如有侵权,请联系删除。
上图的图标都是经过缩小的图标,实际上这些图标的分辨率都是比较大的,例如方形的图标分辨率为 128*128;但是我们 Jz2440 开发板的 LCD 只有 480 * 272,如果按实际图片的尺寸大小显示,显然是不可取的,所以需要进行 图片缩放 ;另外上图图标的格式为 bmp 格式的图标,要在 LCD 上显示这种格式的图片,就涉及到了 bmp 格式的图片解码。
(1) 关于 BMP 格式图片的数据分析可以查看这个两篇博客:bitmap格式分析、BMP图像数据格式详解。
(2) 这里需要注意的是 LCD 是从左上角开始显示图像的,而 BMP 图片像素数据的存储是从左下角开始的,如下图所示。因此在显示BMP图片时需要从BMP图片的左上角开始读取数据发送给LCD显示:
(3) BMP 格式图片解码代码:
① 在 pic_operation.h 中定义像素数据结构体 T_PixelDatas、文件解析结构体 T_PicFileParser,代码如下:
#ifndef _PIC_OPERATION_H
#define _PIC_OPERATION_H
typedef struct PixelDatas{
int iWidth;
int iHeight;
int iBpp;
int iLineBytes;
unsigned char *aucPixelDatas;
}T_PixelDatas, *PT_PixelDatas;
typedef struct PicFileParser{
char *name;
int (*isSupport)(unsigned char *aFileHead);
int (*GetPixelDatas)(unsigned char *aFileHead, PT_PixelDatas tPixelDatas);
int (*FreePixelDatas)(PT_PixelDatas tPixelDatas);
}T_PicFileParser, *PT_PicFileParser;
int PicZoom(PT_PixelDatas ptOriginPic, PT_PixelDatas ptZoomPic);
int PicMerge(int iX, int iY, PT_PixelDatas ptSmallPic, PT_PixelDatas ptBigPic);
#endif /* _PIC_OPERATION_H */
② bmp解码代码如下:bmp.c
#include
#include
#include
#include
#pragma pack(push) /* 将当前pack设置压栈保存 */
#pragma pack(1) /* 必须在结构体定义之前使用 */
/* bmp 文件头部 */
typedef struct tagBITMAPFILEHEADER { /* bmfh */
unsigned short bfType; /* 由于更快的访问bfType, 此时的bfType被4字节对齐 */
unsigned long bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned long bfOffBits;
} BITMAPFILEHEADER;
#pragma pack(pop) /* 恢复先前的pack设置 */
/* bmp 位图信息头部 */
typedef struct tagBITMAPINFOHEADER { /* bmih */
unsigned long biSize;
unsigned long biWidth;
unsigned long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
unsigned long biXPelsPerMeter;
unsigned long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} BITMAPINFOHEADER;
/* bmp 文件开头的标记为 0x4D42
* 以此判断是否支持BMP格式
*/
static int isBMPFormat(unsigned char *aFileHead)
{
if (aFileHead[0] != 0x42 || aFileHead[1] != 0x4D)
{
return 0;
}
else
{
return -1;
}
}
/* 从BMP图片获取一行数据 */
static int ConvertOneLineFrmBMP(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)
{
dwRed = dwRed >> 3;
dwGreen = dwGreen >> 2;
dwBlue = dwBlue >> 3;
dwColor = (dwRed << 11) | (dwGreen << 5) | dwBlue;
*pwDstDatas16bpp = dwColor;
pwDstDatas16bpp++;
}
}
}
return 0;
}
/* 获取BMP图片像素数据
* ptPixelDatas->iBpp 是输入的参数,它决定从BMP得到的数据要转换为该格式
*/
static int GetPixelDatasFrmBMP(unsigned char *aFileHead, PT_PixelDatas ptPixelDatas)
{
BITMAPFILEHEADER *ptBITMAPFILEHEADER;
BITMAPINFOHEADER *ptBITMAPINFOHEADER;
int iWidth;
int iHeight;
int iBMPBpp;
int y;
unsigned char *pucSrc;
unsigned char *pucDest;
int iLineWidthAlign;
int iLineWidthReal;
ptBITMAPFILEHEADER = (BITMAPFILEHEADER *)aFileHead;
ptBITMAPINFOHEADER = (BITMAPINFOHEADER *)(aFileHead + sizeof(BITMAPFILEHEADER));
iWidth = ptBITMAPINFOHEADER->biWidth;
iHeight = ptBITMAPINFOHEADER->biHeight;
iBMPBpp = ptBITMAPINFOHEADER->biBitCount;
if (iBMPBpp != 24)
{
return -1;
}
ptPixelDatas->iWidth = iWidth;
ptPixelDatas->iHeight = iHeight;
ptPixelDatas->aucPixelDatas = malloc(iWidth * iHeight * ptPixelDatas->iBpp / 8);
ptPixelDatas->iLineBytes = iWidth * ptPixelDatas->iBpp / 8;
if (ptPixelDatas->aucPixelDatas == NULL)
{
return -1;
}
iLineWidthReal = iWidth * iBMPBpp / 8;
iLineWidthAlign = (iLineWidthReal + 3) & ~0x03; /* 4字节对齐 */
pucSrc = aFileHead +ptBITMAPFILEHEADER->bfOffBits;
pucSrc = pucSrc + (iHeight - 1) * iLineWidthAlign; /* 把pucSrc指向图片数据的最后一行 */
pucDest = ptPixelDatas->aucPixelDatas;
for (y = 0; y < iHeight; y++)
{
ConvertOneLineFrmBMP(iWidth, iBMPBpp, ptPixelDatas->iBpp, pucSrc, pucDest);
pucSrc -= iLineWidthAlign;
pucDest += ptPixelDatas->iLineBytes;
}
return 0;
}
static int FreePixelDatasForBMP(PT_PixelDatas ptPixelDatas)
{
free(ptPixelDatas->aucPixelDatas);
return 0;
}
T_PicFileParser g_tBMPParser = {
.name = "bmp",
.isSupport = isBMPFormat,
.GetPixelDatas = GetPixelDatasFrmBMP,
.FreePixelDatas = FreePixelDatasForBMP,
};
(1) 关于图像的缩放算法,可以参考博客:图像缩放算法。
(2) 在图像缩放算法这博客中,有写到图片的缩放原理如下图所示:根据原图和缩放后图片的比例相同的原理,通过缩放后图片的 x、y 坐标求出原图的Sx、Sy坐标,然后取出原图(Sx,Sy)坐标的颜色值赋值给缩放图片的(x,y)坐标。
由此可以得出基本的代码框架如下图所示:
由于图片的像素是一行一行的读取的,在上图的 for 的第二层循环中,由于 y 的值是不变的,所有为了节省计算,优化如下:
另外,对与图片的每一列,x 的值是相等的,我们可以事先算出每一列 x 的值,存放在一个 table 里面,这样就不用每执行一次第二层for循环都重新计算 Sx 的值,从而节省计算时间,优化如下:
(3) 图片缩放的代码如下:zoom.c
#include
#include
#include
int PicZoom(PT_PixelDatas ptOriginPic, PT_PixelDatas ptZoomPic)
{
unsigned long dwDstWidth = ptZoomPic->iWidth;
unsigned long *pdwSrcXTable = malloc(sizeof(unsigned long) * dwDstWidth);
unsigned long x;
unsigned long y;
unsigned long dwSrcY;
unsigned char *pucDest;
unsigned char *pucSrc;
unsigned long dwPixelBytes = ptOriginPic->iBpp / 8;
if (ptOriginPic->iBpp != ptZoomPic->iBpp)
{
free(pdwSrcXTable);
return -1;
}
for (x = 0; x < dwDstWidth; x++)
{
pdwSrcXTable[x] = x * ptOriginPic->iWidth / ptZoomPic->iWidth;
}
for (y = 0; y < ptZoomPic->iHeight; y++)
{
dwSrcY = y * ptOriginPic->iHeight / ptZoomPic->iHeight;
pucDest = ptZoomPic->aucPixelDatas + y * ptZoomPic->iLineBytes;
pucSrc = ptOriginPic->aucPixelDatas + dwSrcY * ptOriginPic->iLineBytes;
for (x = 0; x < dwDstWidth; x++)
{
/* 原图坐标:SrcX_Table[x], dwSrcY
* 缩放坐标:x, y
*/
memcpy(pucDest + x * dwPixelBytes , pucSrc + pdwSrcXTable[x] * dwPixelBytes, dwPixelBytes);
}
}
free(pdwSrcXTable);
return 0;
}
图片经过缩小后得到一个小的数组,如何把这个小的数组放到大的 framebuffer里呢?代码如下:merge.c
#include
#include
/* 把小图片合并到大图片的(iX,iY)坐标中
* (iX,iY)坐标是小图片合并到大图的坐标
*/
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;
}
在 main.c 中,显示一幅原图和原图长宽缩小1/2的图片,代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* ./digitpic */
int main(int argc, char **argv)
{
int iFdBmp;
int iRet;
PT_DispOpr ptDispOpr;
unsigned char *pucBMPmem;
struct stat tBMPStat;
extern T_PicFileParser g_tBMPParser;
T_PixelDatas tPixelDatas;
T_PixelDatas tPixelDatasSmall;
T_PixelDatas tPixelDatasFB;
if (argc != 2)
{
printf("%s \n" , argv[0]);
return -1;
}
DebugInit();
InitDebugChannel();
DisplayInit();
ptDispOpr = GetDispOpr("fb");
ptDispOpr->DeviceInit();
ptDispOpr->CleanScreen(0x00);
/* 打开BMP文件 */
iFdBmp = open(argv[1], O_RDWR);
if (iFdBmp == -1)
{
DBG_PRINTF("can't open %s!\n", argv[1]);
return -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_tBMPParser.isSupport(pucBMPmem);
if (iRet == 0)
{
DBG_PRINTF("%s is not bmp file!\n", argv[1]);
return -1;
}
tPixelDatas.iBpp = ptDispOpr->iBpp;
iRet = g_tBMPParser.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);
/* 缩小到原来的1/4 */
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);
free(tPixelDatasSmall.aucPixelDatas);
return 0;
}