RT-Thread:基于STM32F407 读取SD卡、TF卡的JPG解码并LCD显示

说明:文档记录 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

1. 开启文件系统框架、生成SD卡驱动

这部分流程参考文档介绍:

RT-Thread SD卡功能应用:(请参考另一篇文章)

2. 移植 JPG 解码软件包 TJpgDec

1. TJpgDec-R0.03.zip

        软件包包含如下3个文件,使用时本工程直接复制到 PICTURE 文件夹中,pic_example.c 文件夹包含 tjpgd.h 文件,使用时主要使用几个函数,并根据硬件实现软件包的输入、输出的函数。

RT-Thread:基于STM32F407 读取SD卡、TF卡的JPG解码并LCD显示_第1张图片

2. tjpgdcnf.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)
*/

3. tjpgd.c 中的应用函数,主要调用以下两个函数

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) */
)

4. I/O函数#

输入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, ...。

3. JPG 解码应用显示代码

/*------------------------------*/
/* 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,使得解码工作继续执行
}

1. 两个对外函数,显示指定文件,和显示指定目录下的所有文件。

/*------------------------------*/
/* 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[])())

2. 图片显示示例文件,依赖 tjpgd 图片解码库

/*------------------------------------------------*/
/* 图片显示示例文件,依赖 tjpgd 图片解码库       */
/*------------------------------------------------*/

#include 
#include 
#include 
#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;itop+i))>>13;//实际Y坐标
        //在这里不改变picinfo.staticx和picinfo.staticy的值 ,如果在这里改变,则会造成每块的第一个点不显示!!!
        if(!is_element_ok(realx,realy,0))//行值是否满足条件? 寻找满足条件的行
        {
            pencolor+=width;
            continue;
        }
        for(j=0;jleft+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);

4. 网友资料

1.怎么使用

首先,你应该构建和运行如下所示示例程序。这是一个典型的使用TJpgDec模块,它有助于调试和缩小问题。解码会话分为两个阶段。第一阶段是分析JPEG图像,第二阶段是解码。

  1. 初始化输入流。(例如:打开一个文件)
  2. 分配JPEG解码对象和工作区域。
  3. 调用jd_prepare取分析和准备压缩的JPEG图像。
  4. 使用解码对象中的图像信息初始化输出设备。
  5. 调用jd_decomp解码JPEG图像。

2.系统结构

RT-Thread:基于STM32F407 读取SD卡、TF卡的JPG解码并LCD显示_第2张图片

3.示例

/*------------------------------------------------*/
/* TJpgDec Quick Evaluation Program for PCs       */
/*------------------------------------------------*/

#include 
#include 
#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;
}

4.限制

  • JPEG标准:仅基线。渐进和无损JPEG格式不支持。
  • 图像大小:最大65520 x 65520像素。
  • 颜色空间:仅YCbCr三通道。灰度图像不支持。
  • 采样因子:4:4:4、4:2:2或4:2:0。

5.内存使用

这是一些平台在默认配置下的内存占用。编译优化的代码大小。

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... 字节是从存储设备读取的理想大小。

你可能感兴趣的:(RT-Thread,stm32,嵌入式硬件,单片机,RT-Thread,LCD,JPG,JPEG,解码)