说明:文档记录 STM32F407 基于 RT-Thread 系统的 SD 卡 JPG,JPEG 照片解码并LCD显示的流程介绍。
资源链接:
TJpgDec作者源码链接:http://elm-chan.org/fsw/tjpgd/00index.html
网友应用文档:
TJpgDec使用说明: https://www.cnblogs.com/oloroso/p/5852235.html
STM32单片机图片解码:https://www.cnblogs.com/dengxiaojun/p/4374986.html
这部分流程参考文档介绍:
RT-Thread SD卡功能应用:(请参考另一篇文章)
????????软件包包含如下3个文件,使用时本工程直接复制到 PICTURE 文件夹中,pic_example.c 文件夹包含 tjpgd.h 文件,使用时主要使用几个函数,并根据硬件实现软件包的输入、输出的函数。
/*----------------------------------------------*/
/* TJpgDec System Configurations R0.03 */
/*----------------------------------------------*/
#define JD_SZBUF 512
/* Specifies size of stream input buffer 指定流输入缓冲区的大小
* */
#define JD_FORMAT 1
/* Specifies output pixel format.指定输出像素格式。
/ 0: RGB888 (24-bit/pix)
/ 1: RGB565 (16-bit/pix)
/ 2: Grayscale (8-bit/pix) 灰度
*/
#define JD_USE_SCALE 1
/* Switches output descaling feature.
/ 0: Disable
/ 1: Enable
*/
#define JD_TBLCLIP 1
/* Use table conversion for saturation arithmetic. A bit faster, but increases 1 KB of code size.
/ 对饱和算法使用表转换。稍微快一点,但增加了1KB的代码大小
/ 0: Disable
/ 1: Enable
*/
#define JD_FASTDECODE 1
/* Optimization level 优化水平
/ 0: Basic optimization. Suitable for 8/16-bit MCUs. 基本优化。适用于8/16位MCU。
/ 1: + 32-bit barrel shifter. Suitable for 32-bit MCUs. 32 位桶形移位器。 适用于 32 位 MCU。
/ 2: + Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of RAM)能够转换为霍夫曼解码(需要 6 << HUFF_BIT 字节的 RAM)
*/
jd_prepare ();分析JPEG数据并创建一个解码对象(decompression object)用于随后的解码过程。
jd_decomp ();
/*-----------------------------------------------------------------------------------------------*/
/* Analyze the JPEG image and Initialize decompressor object 分析JPEG图像并初始化解压器对象 */
/*-----------------------------------------------------------------------------------------------*/
JRESULT jd_prepare (
JDEC* jd, /* Blank decompressor object 空解压器对象, 指定解码对象去初始化。这个解码对象是用于后续的解码操作。*/
size_t (*infunc)(JDEC*, uint8_t*, size_t) /* JPEG strem input function JPEG 串输入功能, 指定用户定义的数据输入函数。jd_prepare和jd_decomp调用这个函数来从输入流读取JPEG数据.*/
void* pool, /* Working buffer for the decompression session 解压缩的工作缓冲区,指向此会话工作区域的指针。它应该与word边界对齐或者它可以导致一个异常。*/
size_t sz_pool, /* Size of working buffer 解压缓存的大小,指定工作区域的字节数。TJpgDec至多需要3092字节的工作区域,这依赖于JPEG图像的内置参数表。通常情况下是3092字节工作区域.*/
void* dev /* I/O device identifier for the session 会话的I/O设备标识符,指定用户定义的会话设备标识。它保存在解码对象的device成员中。它可以用于I/O函数去识别当前会话。*/
/* 当I/O device固定在project或者不需要这个功能,设置为NULL并忽略它。*/
)
描述
jd_prepare函数是JPEG解码会话的第一阶段。它分析JPEG图像和创建解码参数表。函数成功后,会话准备好在jd_decomp函数解码JPEG图像。
应用程序可以参考JPEG解码对象中存储的尺寸大小。这个信息将用于在后续的解码阶段配置输出设备(device)和参数。
/*-----------------------------------------------------------------------*/
/* Start to decompress the JPEG picture 执行解码JPEG图像 */
/*-----------------------------------------------------------------------*/
/*描述:jd_decomp是JPEG解码会话的第二阶段。它解码JPEG图像并通过用户定义的输出函数输出数据,在它之后,解码对象将不在有效。*/
/*在解码时指定的比例因子,它将JPEG图像按1/2、1/4或1/8比例缩放尺寸。例如,当解码一个1024x768大小JPEG图像在1/4比例,*/
/*它将输出256x192大小。相比不缩放,1/2和1/4的缩放由于求均值,解码速度略有下降。但是1/8缩放相比不缩放是2-3倍的速度输出,*/
/*因为每个块IDCT和求均值可以跳过。这一特点适合创建缩略图。*/
JRESULT jd_decomp (
JDEC* jd, /* Initialized decompression object */
int (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */
uint8_t scale /* Output de-scaling factor (0 to 3) */
)
输入JPEG数据并输出解码后像素,TJpgDec需要用户定义两个I/O函数
Input Function#
Input funciotn?- 从输入流读取JPEG数据
用户定义的从输入流读取数据的输入函数。
UINT in_func (
JDEC* jdec, /* Pointer to the decompression object */
BYTE* buff, /* Pointer to buffer to store the read data */
UINT ndata /* Number of bytes to read */
);
?
参数:
jdec
指定解码会话的解码对象。
buff
指定读缓冲器去保存读取数据。传入NULL将数据从输入流移除。
ndata
指定从输入流读取或移除的字节数。
返回值:
返回读取或移除的字节数。若返回0,jd_prepare和jd_decomp函数将终止并返回JDR_INP。
描述:
这个函数是TJpgDec模块的数据输入接口。可以通过指向设备标识符的指针来标识相应的解码会话。
Output Function#
Output function?- 写像素数据到输出设备
用户定义的输出函数,写解码像素到输出设备。
?
UINT out_func (
JDEC* jdec, /* Pointer to the decompression object */
void* bitmap, /* RGB bitmap to be output */
JRECT* rect /* Rectangular region to output */
);
参数:
jdec
指定会话的解码对象。
bitmap
指定RGB位图(bitmap)用于输出。
rect
指定在图像中的矩形区域去输出RGB位图。
返回值:
通常返回1,以便TJpgDec继续解码过程。当它返回0,jd_decomp函数终止并返回JDR_INTR,这在中断减压过程中有用。
描述:
这个函数是TJpgDec模块的数据输出函数。可以通过指向设备标识符的指针来标识相应的解码会话,jdec->device通过jd_prepare函数第五个参数确定。
在这个函数中,位图发送到帧缓冲或显示设备。第一个像素是位图矩形的左上角位置,最后一个像素是右下角位置。矩形的大小从1x1到16x16取决于图像的裁剪、
缩放和采样因子。如果矩形是帧缓冲区,它将在函数中倍裁剪。
像素格式取决于JD_FORMAT参数的配置选项。当它配置为RGB888,位图是一个字节数组,
每3个字节保存一个RGB像素:RRRRRRRR, GGGGGGGG, BBBBBBBB, RRRRRRRR, GGGGGGGG, BBBBBBBB, ...;
配置为RGB565时,位图是一个WORD数组,RGB数据1word每像素:RRRRRGGGGGGBBBBB, RRRRRGGGGGGBBBBB, RRRRRGGGGGGBBBBB, ...。
/*------------------------------*/
/* tjpgd 用户自定义输入函数*/
/*------------------------------*/
unsigned int in_func (JDEC* jd, uint8_t* buff, unsigned int nbyte)
{
/* Device identifier for the session (5th argument of jd_prepare function)会话的设备标识符(jd_prepare 函数的第 5 个参数) */
IODEV *dev = (IODEV*)jd->device;
if (buff) {
/* Read bytes from input stream 从输入流中读取字节*/
return (uint16_t)fread(buff, 1, nbyte, dev->fp);
} else {
/* Remove bytes from input stream 从输入流中删除字节*/
return fseek(dev->fp, nbyte, SEEK_CUR) ? 0 : nbyte;
}
}
/*------------------------------*/
/* tjpgd 用户自定义输出函数*/
/*------------------------------*/
int out_func (JDEC* jd, void* rgbbuf, JRECT* rect)
{
u16 *pencolor=(u16*)rgbbuf;
u16 width=rect->right-rect->left+1; //填充的宽度
u16 height=rect->bottom-rect->top+1; //填充的高度
pic_phy.fillcolor(rect->left+picinfo.S_XOFF,rect->top+picinfo.S_YOFF,width,height,pencolor);//颜色填充
return 1; //返回0,使得解码工作继续执行
}
/*------------------------------*/
/* Jpeg_Dec 图片解码并显示功能 函数,通过MSH命令输入 Jpeg_Dec + 文件名或包含路径的文件名即可显示图片 */
/* 如: Jpeg_Dec 123.jpg 或者在某个目录下面的 /PICTUER/123.jpg */
/*------------------------------*/
int Jpeg_Dec (int argc, char* argv[])()
/* 目录功能测试 */
/* 功能:读取指定目录下面的所有文件,并显示所有照片文件 */
/* 参数:argc 命令数量,char* argv[] 命令内容,argv[0] 为命令,argv[1] 为文件夹路径 */
int read_dir_pic(int argc, char* argv[])())
/*------------------------------------------------*/
/* 图片显示示例文件,依赖 tjpgd 图片解码库 */
/*------------------------------------------------*/
#include <rtthread.h>
#include <stdio.h>
#include <string.h>
#include "tjpgd.h"
#include "gui.h"
int jpeg_out_func_point(JDEC* jd,void* rgbbuf,JRECT* rect);
int jpeg_out_func_fill(JDEC* jd,void* rgbbuf,JRECT* rect);
u8 jpeg_mallocall(void);
void jpeg_freeall(void);
#define JPEG_WBUF_SIZE 4096 /*定义工作区数组大小,最少应不小于3092字节.*/
u8 *jpg_buffer; /*定义jpeg解码工作区大小(最少需要3092字节),作为解压缓冲区,必须4字节对齐*/
/* 用户定义的设备标识符 */
typedef struct {
FILE *fp; /* File pointer for input function 输入函数的文件指针*/
uint8_t *fbuf; /* Pointer to the frame buffer for output function 指向输出函数的帧缓冲区的指针*/
uint16_t wfbuf; /* Width of the frame buffer [pix] 帧缓冲区的宽度*/
} IODEV;
/*------------------------------*/
/* tjpgd 用户自定义输入函数*/
/*------------------------------*/
unsigned int in_func (JDEC* jd, uint8_t* buff, unsigned int nbyte)
{
/* Device identifier for the session (5th argument of jd_prepare function)会话的设备标识符(jd_prepare 函数的第 5 个参数) */
IODEV *dev = (IODEV*)jd->device;
if (buff) {
/* Read bytes from input stream 从输入流中读取字节*/
return (uint16_t)fread(buff, 1, nbyte, dev->fp);
} else {
/* Remove bytes from input stream 从输入流中删除字节*/
return fseek(dev->fp, nbyte, SEEK_CUR) ? 0 : nbyte;
}
}
/*------------------------------*/
/* tjpgd 用户自定义输出函数*/
/*------------------------------*/
int out_func (JDEC* jd, void* rgbbuf, JRECT* rect)
{
u16 *pencolor=(u16*)rgbbuf;
u16 width=rect->right-rect->left+1; //填充的宽度
u16 height=rect->bottom-rect->top+1; //填充的高度
pic_phy.fillcolor(rect->left+picinfo.S_XOFF,rect->top+picinfo.S_YOFF,width,height,pencolor);//颜色填充
return 1; //返回0,使得解码工作继续执行
}
//采用填充的方式进行图片解码显示
//jd:储存待解码的对象信息的结构体
//rgbbuf:指向等待输出的RGB位图数据的指针
//rect:等待输出的矩形图像的参数
//返回值:0,输出成功;1,输出失败/结束输出
int jpeg_out_func_fill(JDEC* jd,void* rgbbuf,JRECT* rect)
{
u16 *pencolor=(u16*)rgbbuf;
u16 width=rect->right-rect->left+1; //填充的宽度
u16 height=rect->bottom-rect->top+1; //填充的高度
pic_phy.fillcolor(rect->left+picinfo.S_XOFF,rect->top+picinfo.S_YOFF,width,height,pencolor);//颜色填充
return 0; //返回0,使得解码工作继续执行
}
//采用画点的方式进行图片解码显示
//jd:储存待解码的对象信息的结构体
//rgbbuf:指向等待输出的RGB位图数据的指针
//rect:等待输出的矩形图像的参数
//返回值:0,输出成功;1,输出失败/结束输出
int jpeg_out_func_point(JDEC* jd,void* rgbbuf,JRECT* rect)
{
u16 i,j;
u16 realx=rect->left,realy=0;
u16 *pencolor=rgbbuf;
u16 width=rect->right-rect->left+1; //图片的宽度
u16 height=rect->bottom-rect->top+1; //图片的高度
for(i=0;i<height;i++)//y坐标
{
realy=(picinfo.Div_Fac*(rect->top+i))>>13;//实际Y坐标
//在这里不改变picinfo.staticx和picinfo.staticy的值 ,如果在这里改变,则会造成每块的第一个点不显示!!!
if(!is_element_ok(realx,realy,0))//行值是否满足条件? 寻找满足条件的行
{
pencolor+=width;
continue;
}
for(j=0;j<width;j++)//x坐标
{
realx=(picinfo.Div_Fac*(rect->left+j))>>13;//实际X坐标
//在这里改变picinfo.staticx和picinfo.staticy的值
if(!is_element_ok(realx,realy,1))//列值是否满足条件? 寻找满足条件的列
{
pencolor++;
continue;
}
pic_phy.draw_point(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF,*pencolor);//显示图片
pencolor++;
}
}
return 1; //返回1,使得解码工作继续执行
}
/* 获取文件大小 */
/* 参数:void *file 文件路径 */
/* 返回: 文件大小 字节 */
static uint32_t stat_file(void *file)
{
int ret;
struct stat buf;
ret = stat((char *)file, &buf);
if(ret == 0)
{
rt_kprintf("文件: %s 文件大小= %d 字节 \n",(char *)file, buf.st_size);
return buf.st_size;
}
else
{
rt_kprintf("文件: %s 没有找到 \n",(char *)file);
return 0;
}
}
/*------------------------------*/
/* Jpeg_Dec 图片解码并显示功能 函数,通过MSH命令输入 Jpeg_Dec + 文件名或包含路径的文件名即可显示图片 */
/* 如: Jpeg_Dec 123.jpg 或者在某个目录下面的 /PICTUER/123.jpg */
/*------------------------------*/
int Jpeg_Dec (int argc, char* argv[])
{
JDEC jdec; /* Decompression object */
JRESULT res = 0; /* Result code of TJpgDec API */
IODEV devid; /* User defined device identifier */
u8 scale; /*图像输出比例 0,1/2,1/4,1/8*/
JDEC *jpeg_dev; /*待解码对象结构体指针*/
int (*outfun)(JDEC*, void*, JRECT*); /* 输出回调函数指针 */
u8 fast = 0; /*使能jpeg/jpg小图片(图片尺寸小于等于液晶分辨率)快速解码,0,不使能;1,使能. */
uint32_t file_size = 0; /*文件大小 字节*/
piclib_init(); /*初始化画图*/
jpeg_dev = &jdec;
/* 检查 Jpeg_Dec 命令输入的参数 */
if (argc < 2)
{
rt_kprintf("Jpeg_Dec 参数错误 ...\n");
return -1;
}
/* 打开文件 */
file_size = stat_file(argv[1]);
devid.fp = fopen(argv[1], "rb"); /*rb+ 读写打开一个二进制文件,允许读数据。*/
if (!devid.fp)
{
rt_kprintf("Jpeg_Dec 打开文件失败...\n");
rt_kprintf("Jpeg_Dec 文件名:%s \n",argv[1]);
return -1;
}
/* 为 TJpgDec 分配工作区*/
jpg_buffer = rt_malloc(JPEG_WBUF_SIZE);
if(jpg_buffer == RT_NULL)
{
rt_kprintf("Jpeg_Dec 内存申请失败...\n");
res = 5;
goto __exit;
}
ai_load_picfile(0,0,lcddev.width,lcddev.height,1);/*智能画图,根据图片尺寸智能设置图片显示位置*/
LCD_Clear(BLACK); //清屏
if (jpg_buffer != RT_NULL)
{
/*YL 下*/
res = jd_prepare(jpeg_dev,in_func,jpg_buffer,JPEG_WBUF_SIZE,&devid);//执行解码的准备工作,调用TjpgDec模块的jd_prepare函数
outfun = jpeg_out_func_point;//默认采用画点的方式显示
if(res==JDR_OK)//准备解码成功
{
for(scale=0;scale<4;scale++)//确定输出图像的比例因子
{
if((jpeg_dev->width>>scale)<=picinfo.S_Width&&(jpeg_dev->height>>scale)<=picinfo.S_Height)//在目标区域内
{
if(((jpeg_dev->width>>scale)!=picinfo.S_Width)&&((jpeg_dev->height>>scale)!=picinfo.S_Height&&scale))scale=0;//不能贴边,则不缩放
else outfun=jpeg_out_func_fill; //在显示尺寸以内,可以采用填充的方式显示
break;
}
}
if(scale==4)scale=0;//错误
if(fast==0)//不需要快速解码
{
outfun=jpeg_out_func_point;//默认采用画点的方式显示
}
picinfo.ImgHeight=jpeg_dev->height>>scale; //缩放后的图片尺寸
picinfo.ImgWidth=jpeg_dev->width>>scale; //缩放后的图片尺寸
ai_draw_init(); //初始化智能画图
//执行解码工作,调用TjpgDec模块的jd_decomp函数
res=jd_decomp(jpeg_dev,outfun,scale);
}
Show_Str(2,2,BRRED,16,(u8 *)argv[1],16,0); //显示图片名字
Show_Str(2,20,BRRED,16,(u8 *)"大小(kb):",16,0); //显示图片名字
Gui_ShowFloat(76,20,(float)file_size/1024,3,16,0,BRRED,16);
rt_kprintf(" 显示完成 .. res = %d ,文件名:%s \n ",res,argv[1]);
/*YL 上*/
}
else
{
rt_kprintf("内存申请失败: rc=%d\n", res);
}
__exit:
if(jpg_buffer != RT_NULL)
{
rt_free(jpg_buffer); /* 释放内存 */
}
fclose(devid.fp); /* 关闭JPG文件 */
return res;
}
MSH_CMD_EXPORT(Jpeg_Dec, Jpeg Decode Test);
#define FILE_DIR "/" /* 文件夹路径 */
//#define FILE_DIR "/PICTURE" /* 文件夹路径 */
#define FILE_DIR_LEN 100 /* 存储 文件路径+文件名 缓存的长度*/
/* 目录功能测试 */
/* 功能:读取指定目录下面的所有文件,并显示所有照片文件 */
/* 参数:argc 命令数量,char* argv[] 命令内容,argv[0] 为命令,argv[1] 为文件夹路径 */
int read_dir_pic(int argc, char* argv[])
{
DIR *dirp;
struct dirent *d;
char *argv_out[2]; /* 调用其他具有 MSH 功能函数时使用的数组 */
char *f_name; /* 文件路径 */
char *name_temp;
int res = 0; /* 返回值 */
/* 检查 read_dir_pic 命令输入的参数 */
if (argc < 2)
{
rt_kprintf("read_dir_pic 参数错误 ...\n");
return -1;
}
f_name = rt_malloc(FILE_DIR_LEN); /* 申请内存 */
if(f_name == RT_NULL)
{
rt_kprintf("f_name 内存申请失败...\n");
res = 0;
goto __exit;
}
/* 打开 / dir_test 目录 */
dirp = opendir(argv[1]); /*打开文件夹 */
if (dirp == RT_NULL)
{
rt_kprintf("目录打开失败! %s\n",argv[1]);
}
else
{
/* 读取目录下的所有文件如果是照片就显示 */
while ((d = readdir(dirp)) != RT_NULL)
{
name_temp = strrchr(d->d_name,'.');/* 查找文件名中最后一次出现‘.’的位置,用来找文件后缀名用 */
/* 判断文件后缀是否是 .jpg 和 .jpeg */
if (((*(name_temp + 1)=='j')&(*(name_temp + 2)=='p')&(*(name_temp + 3)=='g'))||
((*(name_temp + 1)=='j')&(*(name_temp + 2)=='p')&(*(name_temp + 3)=='e')&(*(name_temp + 3)=='g')))
{
memset(f_name,0, FILE_DIR_LEN); /* 清空文件路径缓存 */
strcpy(f_name,argv[1] ); /* 把文件夹路径复制到缓存 */
strcat(f_name, "/"); /* 把文件夹路径后面增加 “/” 以便下一步在后面继续增加文件名 */
strncat(f_name, d->d_name,d->d_reclen); /* 把文件名增加到路径缓存 */
argv_out[1] = f_name;
rt_kprintf("查到文件: %s\n", d->d_name);
Jpeg_Dec (2,argv_out); /* 调用图片显示函数,显示指定文件名的文件 */
rt_thread_mdelay(2000);
}
}
closedir(dirp); /* 关闭目录 */
res = 1;
}
__exit:
if(f_name != RT_NULL)
{
rt_free(f_name); /* 释放内存 */
}
return res;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(read_dir_pic, 显示文件夹下图片);
///* 目录功能测试 */
///* 读取指定目录下面的所有文件,并显示所有照片文件 */
//uint8_t readdir_sample(void)
//{
// DIR *dirp;
// struct dirent *d;
// char *argv[2];
// char *f_name; /* 文件路径 */
// char *name_temp;
// uint8_t res = 0; /* 返回值 */
//
// f_name = rt_malloc(FILE_DIR_LEN); /* 申请内存 */
// if(f_name == RT_NULL)
// {
// rt_kprintf("f_name 内存申请失败...\n");
// res = 0;
// goto __exit;
// }
//
//
// /* 打开 / dir_test 目录 */
// dirp = opendir(FILE_DIR); /*打开文件夹 */
//
// if (dirp == RT_NULL)
// {
// rt_kprintf("目录打开失败! %s\n",FILE_DIR_LEN);
// }
// else
// {
// /* 读取目录下的所有文件如果是照片就显示 */
// while ((d = readdir(dirp)) != RT_NULL)
// {
// name_temp = strrchr(d->d_name,'.');/* 查找文件名中最后一次出现‘.’的位置,用来找文件后缀名用 */
// /* 判断文件后缀是否是 .jpg 和 .jpeg */
// if (((*(name_temp + 1)=='j')&(*(name_temp + 2)=='p')&(*(name_temp + 3)=='g'))||
// ((*(name_temp + 1)=='j')&(*(name_temp + 2)=='p')&(*(name_temp + 3)=='e')&(*(name_temp + 3)=='g')))
// {
// memset(f_name,0, FILE_DIR_LEN); /* 清空文件路径缓存 */
// strcpy(f_name,FILE_DIR ); /* 把文件夹路径复制到缓存 */
// strcat(f_name, "/"); /* 把文件夹路径后面增加 “/” 以便下一步在后面继续增加文件名 */
// strncat(f_name, d->d_name,d->d_reclen); /* 把文件名增加到路径缓存 */
// argv[1] = f_name;
//
// rt_kprintf("查到文件: %s\n", d->d_name);
// Jpeg_Dec (2,argv); /* 调用图片显示函数,显示指定文件名的文件 */
// rt_thread_mdelay(2000);
// }
//
// }
// closedir(dirp); /* 关闭目录 */
//
// }
//
// __exit:
// if(f_name != RT_NULL)
// {
// rt_free(f_name); /* 释放内存 */
// }
// return res;
//}
///* 导出到 msh 命令列表中 */
//MSH_CMD_EXPORT(readdir_sample, readdir sample);
首先,你应该构建和运行如下所示示例程序。这是一个典型的使用TJpgDec模块,它有助于调试和缩小问题。解码会话分为两个阶段。第一阶段是分析JPEG图像,第二阶段是解码。
/*------------------------------------------------*/
/* TJpgDec Quick Evaluation Program for PCs */
/*------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include "tjpgd.h"
/* 用户定义设备标识 */
typedef struct {
FILE *fp; /* 用于输入函数的文件指针 */
BYTE *fbuf; /* 用于输出函数的帧缓冲区的指针 */
UINT wfbuf; /* 帧缓冲区的图像宽度[像素] */
} IODEV;
/*------------------------------*/
/* 用户定义input funciton */
/*------------------------------*/
UINT in_func (JDEC* jd, BYTE* buff, UINT nbyte)
{
IODEV *dev = (IODEV*)jd->device; /* Device identifier for the session (5th argument of jd_prepare function) */
if (buff) {
/* 从输入流读取一字节 */
return (UINT)fread(buff, 1, nbyte, dev->fp);
} else {
/* 从输入流移除一字节 */
return fseek(dev->fp, nbyte, SEEK_CUR) ? 0 : nbyte;
}
}
/*------------------------------*/
/* 用户定义output funciton */
/*------------------------------*/
UINT out_func (JDEC* jd, void* bitmap, JRECT* rect)
{
IODEV *dev = (IODEV*)jd->device;
BYTE *src, *dst;
UINT y, bws, bwd;
/* 输出进度 */
if (rect->left == 0) {
printf("\r%lu%%", (rect->top << jd->scale) * 100UL / jd->height);
}
/* 拷贝解码的RGB矩形范围到帧缓冲区(假设RGB888配置) */
src = (BYTE*)bitmap;
dst = dev->fbuf + 3 * (rect->top * dev->wfbuf + rect->left); /* 目标矩形的左上 */
bws = 3 * (rect->right - rect->left + 1); /* 源矩形的宽度[字节] */
bwd = 3 * dev->wfbuf; /* 帧缓冲区宽度[字节] */
for (y = rect->top; y <= rect->bottom; y++) {
memcpy(dst, src, bws); /* 拷贝一行 */
src += bws; dst += bwd; /* 定位下一行 */
}
return 1; /* 继续解码 */
}
/*------------------------------*/
/* 主程序 */
/*------------------------------*/
int main (int argc, char* argv[])
{
void *work; /* 指向解码工作区域 */
JDEC jdec; /* 解码对象 */
JRESULT res; /* TJpgDec API的返回值 */
IODEV devid; /* 用户定义设备标识 */
/* 打开一个JPEG文件 */
if (argc < 2) return -1;
devid.fp = fopen(argv[1], "rb");
if (!devid.fp) return -1;
/* 分配一个用于TJpgDec的工作区域 */
work = malloc(3100);
/* 准备解码 */
res = jd_prepare(&jdec, in_func, work, 3100, &devid);
if (res == JDR_OK) {
/* 准备好解码。图像信息有效 */
printf("Image dimensions: %u by %u. %u bytes used.\n", jdec.width, jdec.height, 3100 - jdec.sz_pool);
devid.fbuf = malloc(3 * jdec.width * jdec.height); /* 输出图像的帧缓冲区(假设RGB888配置) */
devid.wfbuf = jdec.width;
res = jd_decomp(&jdec, out_func, 0); /* 开始1/1缩放解码 */
if (res == JDR_OK) {
/* 解码成功。你在这里已经解码图像到帧缓冲区 */
printf("\rOK \n");
} else {
printf("Failed to decompress: rc=%d\n", res);
}
free(devid.fbuf); /* 释放帧缓冲区 */
} else {
printf("Failed to prepare: rc=%d\n", res);
}
free(work); /* 释放工作区域 */
fclose(devid.fp); /* 关闭JPEG文件 */
return res;
}
这是一些平台在默认配置下的内存占用。编译优化的代码大小。
AVR | PIC24 | CM0 | IA-32 |
编译器 | GCC | C30 | GCC |
程序大小 | 9422 | 6544 | 4536 |
????????至于工作区,TJpgDec至多需要3100字节用于JPEG图像。这完全取决于什么样的参数被用来创建JPEG图像。3100字节是在默认输入缓存(JD_SZBUF == 512)下的最大内存需求,并随JD_SZBUF变化。JD_SZBUF定义每次从输入流中读取多少字节。TJpgDec对齐每个读请求缓冲区大小,512, 1024, 2048... 字节是从存储设备读取的理想大小。