1.render/format文件夹
这个文件夹主要是用来识别图片文件、从图片文件中提取数据像素,所用到两个结构体如下
/* 用来描述一张图片的象素数据 */
typedef struct PixelDatas {
int iWidth; /* 宽度: 一行有多少个象素 */
int iHeight; /* 高度: 一列有多少个象素 */
int iBpp; /* 一个象素用多少位来表示 */
int iLineBytes; /* 一行数据有多少字节 */
int iTotalBytes; /* 所有字节数 */
unsigned char *aucPixelDatas; /* 象素数据存储的地方 */
}T_PixelDatas, *PT_PixelDatas;
/* 用来描述一个图片解析器(解析BMP还是JPG) */
typedef struct PicFileParser {
char *name; /* 图片文件解析模块的名字 */
int (*isSupport)(PT_FileMap ptFileMap); /* 是否支持某文件 */
int (*GetPixelDatas)(PT_FileMap ptFileMap, PT_PixelDatas ptPixelDatas); /* 从文件中解析出图像的象素数据 */
int (*FreePixelDatas)(PT_PixelDatas ptPixelDatas); /* 释放图像的象素数据所占内存 */
struct PicFileParser *ptNext; /* 链表 */
}T_PicFileParser, *PT_PicFileParser;
1.picfmt_manager.c中的函数
这个文件里主要是图片解析器的框架,如之前的一样,包含
- 注册一个解释器
- 解释器的初始化
- 打印注册过的解释器
- 根据名字寻找对应解释器的结构体
- 输入一个图片文件寻找合适的解释器
int RegisterPicFileParser(PT_PicFileParser ptPicFileParser);
void ShowPicFmts(void);
int PicFmtsInit(void);
PT_PicFileParser Parser(char *pcName); /* 根据名字寻找解析器*/
PT_PicFileParser GetParser(PT_FileMap ptFileMap); /* 输入一个图片文件,找到可以解析的一个解析器 */
2.jpg.c中的函数
libjpeg库的使用这里不做赘述,最简单的使用方法在bmp和jpg图片显示中有讲,这里不对libjpeg库的使用做详细介绍。
完成jpg解析器的底层,只需要完成typedef struct PicFileParser这个结构体即可
static T_PicFileParser g_tJPGParser = {
.name = "jpg",
.isSupport = isJPGFormat,
.GetPixelDatas = GetPixelDatasFrmJPG,
.FreePixelDatas = FreePixelDatasForJPG,
};
- GetPixelDatasFrmJPG函数
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;
/* 16bpp与32bpp 的指针,这样就可以自加自减了,指向的是目标地址,就是PT_PixelDatas结构体里的aucPixelDatas*/
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) /* 源肯定是24bpp,如果目标bpp是24,那么直接memcpy*/
{
memcpy(pudDstDatas, pudSrcDatas, iWidth*3);
}
else
{
for (i = 0; i < iWidth; i++)
{
/* 一个像素一个像素来,一共来一行有多少个像素次 */
/* 这是一个像素的三个字节的rgb信息 */
dwRed = pudSrcDatas[pos++];
dwGreen = pudSrcDatas[pos++];
dwBlue = pudSrcDatas[pos++];
if (iDstBpp == 32)
{
/* 转成32bpp的 */
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++;
}
}
}
return 0;
}
static int GetPixelDatasFrmJPG(PT_FileMap ptFileMap, PT_PixelDatas ptPixelDatas)
{
struct jpeg_decompress_struct tDInfo;
//struct jpeg_error_mgr tJErr;
int iRet;
int iRowStride;
unsigned char *aucLineBuffer = NULL;
unsigned char *pucDest;
T_MyErrorMgr tJerr;
fseek(ptFileMap->tFp, 0, SEEK_SET);
//tDInfo.err = jpeg_std_error(&tJErr);
tDInfo.err = jpeg_std_error(&tJerr.pub);
tJerr.pub.error_exit = MyErrorExit;
if(setjmp(tJerr.setjmp_buffer))
{
/* 如果程序能运行到这里, 表示JPEG解码出错 */
jpeg_destroy_decompress(&tDInfo);
if (aucLineBuffer)
{
free(aucLineBuffer);
}
if (ptPixelDatas->aucPixelDatas)
{
free(ptPixelDatas->aucPixelDatas);
}
return -1;
}
// 分配和初始化一个decompression结构体
jpeg_create_decompress(&tDInfo);
/* 指定源文件 */
jpeg_stdio_src(&tDInfo, ptFileMap->tFp);
// 用jpeg_read_header获得jpg信息
iRet = jpeg_read_header(&tDInfo, TRUE);
// 设置解压参数,比如放大、缩小
/* 这里就按原来的大小来整,因为后面有放大和缩小的函数来处理 */
tDInfo.scale_num = tDInfo.scale_denom = 1;
//启动解压:jpeg_start_decompress
jpeg_start_decompress(&tDInfo);
//一行数据的长度,单位Bytes,一个output_components一字节
iRowStride = tDInfo.output_width * tDInfo.output_components;
//这个相当于一个缓存,先解押出数据放到这里面,然后再转换成需要的数据,放到别处
aucLineBuffer = malloc(iRowStride);
if (NULL == aucLineBuffer)
{
return -1;
}
ptPixelDatas->iWidth = tDInfo.output_width;
ptPixelDatas->iHeight = tDInfo.output_height;
//ptPixelDatas->iBpp = iBpp; /* 这个是目标bpp,由显示设备决定,所以由上层函数传进来*/
ptPixelDatas->iLineBytes = ptPixelDatas->iWidth * ptPixelDatas->iBpp / 8;
ptPixelDatas->iTotalBytes = ptPixelDatas->iHeight * ptPixelDatas->iLineBytes;
/* 这里申请了一块内存,用于存放从jpg图片中读到的数据,在FreePixelDatasForJPG函数里面释放 */
ptPixelDatas->aucPixelDatas = malloc(ptPixelDatas->iTotalBytes);
if (NULL == ptPixelDatas->aucPixelDatas)
{
return -1;
}
/* 这里之所以还声明了一个变量,是因为这里的aucPixelDatas不能变,一直指向一个地方
* 而后续写数据是需要移动指针的,所以这里又声明了一个指针
*/
pucDest = ptPixelDatas->aucPixelDatas;
/* 循环调用jpeg_read_scanlines来一行一行地获得解压的数据 */
while (tDInfo.output_scanline < tDInfo.output_height)
{
/* 得到一行数据,里面的颜色格式为0xRR, 0xGG, 0xBB,bpp是24的 */
(void) jpeg_read_scanlines(&tDInfo, &aucLineBuffer, 1);
/* 一行一行转到pucDest,也就是ptPixelDatas->aucPixelDatas里*/
CovertOneLine(ptPixelDatas->iWidth, 24, ptPixelDatas->iBpp, aucLineBuffer, pucDest);
/* 一行完了,开始下一行 */
pucDest += ptPixelDatas->iLineBytes;
}
/* 程序至此,就已经从ptFileMap文件中拿出了所有的数据,放到了ptPixelDatas结构体里 */
/* 释放掉刚刚申请的一块内存 */
free(aucLineBuffer);
jpeg_finish_decompress(&tDInfo);
jpeg_destroy_decompress(&tDInfo);
return 0;
}
- FreePixelDatasForJPGhanshu
static int FreePixelDatasForJPG(PT_PixelDatas ptPixelDatas)
{
/* 在GetPixelDatasFrmJPG函数中申请的数据,用于存放jpg数据的 */
free(ptPixelDatas->aucPixelDatas);
return 0;
}
3.bmp.c
从bmp文件获取并转换成我们需要的像素数据,在之前的博客中已经详细写明:bmp和jpg图片显示
2.render/operation文件夹
这个文件夹包括两个:
- 一个是zoom.c,是用来将一块图片信息(已经转成我们需要的图片信息了)放大或者缩小。
- 另一个是merge.c,用来把小图片合并到大图片上去(小图片整个合并到大图片上某个位置,入参xy决定这个位置)
这两个功能,在bmp和jpg图片显示也已经讲清楚。
merge.c中的PicMergeRegion函数
这个函数的功能是,从一张图片上扣下来一块儿,按在另外一张图片的某个地方,一比一的移动
/* 把ptNewPic图片上从(iStartXofNewPic,iStartYofNewPic)开始
* iWidth,iHeight这么大的一块区域扣下来
* 合并到ptOldPic这个图片的(iStartXofOldPic,iStartYofOldPic)这个位置去
*/
int PicMergeRegion(int iStartXofNewPic, int iStartYofNewPic, int iStartXofOldPic, int iStartYofOldPic, int iWidth, int iHeight, PT_PixelDatas ptNewPic, PT_PixelDatas ptOldPic)
{
int i;
unsigned char *pucSrc;
unsigned char *pucDst;
int iLineBytesCpy = iWidth * ptNewPic->iBpp / 8;
/* 判断,如果xy的坐标超过了原图片的范围,就是输入参数有问题 */
if ((iStartXofNewPic < 0 || iStartXofNewPic >= ptNewPic->iWidth) || \
(iStartYofNewPic < 0 || iStartYofNewPic >= ptNewPic->iHeight) || \
(iStartXofOldPic < 0 || iStartXofOldPic >= ptOldPic->iWidth) || \
(iStartYofOldPic < 0 || iStartYofOldPic >= ptOldPic->iHeight))
{
return -1;
}
/* 计算源和目的两个的基地址
* 计算方式是:图片数据的源地址 + 坐标*LineByets + x*bpp/8
*/
pucSrc = ptNewPic->aucPixelDatas + iStartYofNewPic * ptNewPic->iLineBytes + iStartXofNewPic * ptNewPic->iBpp / 8;
pucDst = ptOldPic->aucPixelDatas + iStartYofOldPic * ptOldPic->iLineBytes + iStartXofOldPic * ptOldPic->iBpp / 8;
/* 一行一行的直接memcpy */
for (i = 0; i < iHeight; i++)
{
memcpy(pucDst, pucSrc, iLineBytesCpy);
/* 每次都加自己那张图片的一行的数据量就行 */
pucSrc += ptNewPic->iLineBytes;
pucDst += ptOldPic->iLineBytes;
}
return 0;
}