HC32F460移植JPEG官方库IJG解码JPEG图片 RT-THREAD NANO

HC32F460移植JPEG官方库IJG解码JPEG图片

前言

通常MCU的UI界面,总是需要一些图片资源的。

BMP解码简单,但是非常占用体积,

直接图片数组,无需解码,但是不灵活,很难做后续更新

JPEG体积小,需要MCU一定的速度和计算能力

所幸HC32F460计算速度还是可以的,毕竟200MHZ摆在那里,所以我们选择JPEG作为图片资源,实时解码

JPEG解码,在网络上常规有2种库,一种体积小,移植简单,占用资源小,但是解码据说会慢一些,LVGL里面就是用这个库
在这里插入图片描述
另外一个库,据说就是JPEG官方提供的IJG,网址https://ijg.org/
这个网址进去,页面真是前所未有的简单,感觉回到了20年前的网络,
技术走到尽头就是一切至简
在这里插入图片描述
这个库据说占用资源略大,但是效率高,不管那么多了,第一次移植JPEG,当然要优先选用官方库,点击第一个zip文件,下载就是了

一、开发环境

HC32F460,SDIO 16G TF CARD,QVGA 320x240 ST7789V LCD DISPLAY,KEIL MDK,RTOS RT-THREAD NANO,JPEG IJG,FATFS

二、使用步骤

IPG库也是至简呐。。。不是代码至简,而是作者没分文件夹,197个文件,各个类型都扔在一个文件夹里面,没有任何分类,我也是服了
在这里插入图片描述
真心花了一点时间,才整理出我们解码需要的文件,一共是55个文件,下面截图里面是54个文件,还有个jdatasrc.c,需要改写,放在了外面

在这里插入图片描述
jerror.c和jmemnobs.c是需要简单修改接口的,我们改为jerror_1.c和jmemnobs_1.c
jerror.c修改部分如下
IJG解码异常,默认会输出异常信息,这里修改为rt_kprintf来串口打印

#include "rtthread.h"

METHODDEF(void)
output_message (j_common_ptr cinfo)
{
  char buffer[JMSG_LENGTH_MAX];

  /* Create the message */
  (*cinfo->err->format_message) (cinfo, buffer);

#ifdef USE_WINDOWS_MESSAGEBOX
  /* Display it in a message dialog box */
  MessageBox(GetActiveWindow(), buffer, "JPEG Library Error",
	     MB_OK | MB_ICONERROR);
#else
  /* Send it to stderr, adding a newline */
  rt_kprintf("%s\r\n", buffer);
#endif
}

jmemnobs.c修改如下
IJG解码过程中,需要malloc和free一些ram,很简单,全部替换成rt_malloc, rt_free

#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"
#include "jmemsys.h"		/* import the system-dependent declarations */
#include "rtthread.h"

#ifndef HAVE_STDLIB_H		/* <stdlib.h> should declare malloc(),free() */
extern void * malloc JPP((size_t size));
extern void free JPP((void *ptr));
#endif


/*
 * Memory allocation and freeing are controlled by the regular library
 * routines malloc() and free().
 */

GLOBAL(void *)
jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
{
    return (void *) rt_malloc(sizeofobject);
}

GLOBAL(void)
jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
{
    rt_free(object);
}


/*
 * "Large" objects are treated the same as "small" ones.
 * NB: although we include FAR keywords in the routine declarations,
 * this file won't actually work in 80x86 small/medium model; at least,
 * you probably won't be able to process useful-size images in only 64KB.
 */

GLOBAL(void FAR *)
jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
{
    return (void FAR *) rt_malloc(sizeofobject);
}

GLOBAL(void)
jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
{
    rt_free(object);
}


/*
 * This routine computes the total memory space available for allocation.
 */

GLOBAL(long)
jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed,
		    long max_bytes_needed, long already_allocated)
{
  if (cinfo->mem->max_memory_to_use)
    return cinfo->mem->max_memory_to_use - already_allocated;

  /* Here we say, "we got all you want bud!" */
  return max_bytes_needed;
}


/*
 * Backing store (temporary file) management.
 * Since jpeg_mem_available always promised the moon,
 * this should never be called and we can just error out.
 */

GLOBAL(void)
jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info,
			 long total_bytes_needed)
{
  ERREXIT(cinfo, JERR_NO_BACKING_STORE);
}


/*
 * These routines take care of any system-dependent initialization and
 * cleanup required.  Here, there isn't any.
 */

GLOBAL(long)
jpeg_mem_init (j_common_ptr cinfo)
{
  return 0;			/* just set max_memory_to_use to 0 */
}

GLOBAL(void)
jpeg_mem_term (j_common_ptr cinfo)
{
  /* no work */
}

费点力气就是jdatasrc.c了,这个和jpeg数据有关,文件里包含了两种jpeg数据输入方式
jpeg_stdio_src - 边读边解码,比较适合大图片小内存
jpeg_mem_src - JPEG文件一次性全部读入到BUF,然后开始解码

考虑到解码速度和320X240的JPEG文件只有几十K,所以我们使用jpeg_mem_src ,直接把相关代码复制出来,然后和jpeg显示代码并为一起jpegdisplay.c


#include <stdio.h>
#include <setjmp.h>
#include "jinclude.h"
#include "jpeglib.h"
#include "jerror.h"
#include "ff.h"
#include "ff_ex.h"
#include "st7789v.h"

//#define DBG
#define DBG_TAG             "jpegdisp"
#include "log.h"

struct my_error_mgr 
{
    struct jpeg_error_mgr pub;	/* "public" fields */
    jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

void my_error_exit(j_common_ptr cinfo)
{
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    my_error_ptr myerr = (my_error_ptr) cinfo->err;

    /* Always display the message. */
    /* We could postpone this until after returning, if we chose. */
    (*cinfo->err->output_message) (cinfo);

    /* Return control to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
}

static void init_mem_source (j_decompress_ptr cinfo)
{
  /* no work necessary here */
}

static boolean fill_mem_input_buffer (j_decompress_ptr cinfo)
{
    static const JOCTET mybuffer[4] = {
        (JOCTET) 0xFF, (JOCTET) JPEG_EOI, 0, 0
    };

    cinfo->src->next_input_byte = mybuffer;
    cinfo->src->bytes_in_buffer = 2;

    return TRUE;
}

static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
    struct jpeg_source_mgr * src = cinfo->src;
    size_t nbytes;

    if (num_bytes > 0)
    {
        nbytes = (size_t) num_bytes;
        while (nbytes > src->bytes_in_buffer) 
        {
            nbytes -= src->bytes_in_buffer;
            (void) (*src->fill_input_buffer) (cinfo);
        }
        src->next_input_byte += nbytes;
        src->bytes_in_buffer -= nbytes;
    }
}

static void term_source (j_decompress_ptr cinfo)
{
  /* no work necessary here */
}

/*
 * Prepare for input from a supplied memory buffer.
 * The buffer must contain the whole JPEG data.
 */

void jpeg_mem_src(j_decompress_ptr cinfo, const unsigned char * inbuffer, size_t insize)
{
    struct jpeg_source_mgr * src;

    if (inbuffer == NULL || insize == 0)	/* Treat empty input as fatal error */
    {
        ERREXIT(cinfo, JERR_INPUT_EMPTY);
    }
    
    /* The source object is made permanent so that a series of JPEG images
     * can be read from the same buffer by calling jpeg_mem_src only before
     * the first one.
     */
    if (cinfo->src == NULL) 
    {	/* first time for this JPEG object? */
        cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
            ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(struct jpeg_source_mgr));
    }

    src = cinfo->src;
    src->init_source       = init_mem_source;
    src->fill_input_buffer = fill_mem_input_buffer;
    src->skip_input_data   = skip_input_data;
    src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
    src->term_source       = term_source;
    src->bytes_in_buffer   = insize;
    src->next_input_byte   = (const JOCTET *) inbuffer;
}

static struct jpeg_decompress_struct cinfo;
static struct my_error_mgr jerr;

int jpeg_read_file (char * filename)
{
    static JSAMPARRAY buffer;		/* Output row buffer */
    int row_stride;		    /* physical row width in output buffer */

    FIL *fil = NULL;
    uint32_t insize = 0;
    uint8_t *inbuff = NULL;
    uint32_t br = 0;

    fil = rt_malloc(sizeof(FIL));
    if (fil == NULL)
    {
        LOG_D("jpeg malloc FIL fail\r\n");
        goto err;
    }
    
    if (f_open(fil, filename, FA_OPEN_EXISTING|FA_READ) != FR_OK)
    {
        LOG_D("fat open %s fail\r\n", filename);
        goto err;
    }
  
    insize = f_size(fil);
    LOG_D("fat open %s ok, size - %d\r\n", filename, insize);

    inbuff = rt_malloc(insize);
    if (inbuff == NULL)
    {
        LOG_D("jpeg malloc inbuff fail\r\n");
        goto err;
    }

    f_read(fil, inbuff, insize, &br);
    f_close(fil);
    if (br != insize)
    {
        LOG_D("fat read %s fail\r\n");
        goto err;
    }
    LOG_D("fat read %s pass, size - %d\r\n", filename, insize);
 
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer))
    {
        jpeg_destroy_decompress(&cinfo);
        LOG_D("jpeg decompress setjmp fail\r\n");
        goto err;
    }
 
    LOG_D("jpeg decompress init\r\n");
    jpeg_create_decompress(&cinfo);
    jpeg_mem_src(&cinfo, inbuff, insize);
 
    LOG_D("jpeg read header\r\n");
    jpeg_read_header(&cinfo, TRUE);
   
    LOG_D("jpeg decompress start\r\n");
    jpeg_start_decompress(&cinfo);
  
    row_stride = cinfo.output_width * cinfo.output_components;

    buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
    
    LOG_D("jpeg row_stride %d, buf %08x, image height %d, image width %d\r\n", 
        row_stride, buffer, cinfo.output_height, cinfo.output_width);

    st7789v_set_block(0, 0, cinfo.output_width, cinfo.output_height);
    while (cinfo.output_scanline < cinfo.output_height) 
    {
        jpeg_read_scanlines(&cinfo, buffer, 1);
        st7789v_blit_rowstride_888(*buffer, row_stride);
    }

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    LOG_D("jpeg decompress finish\r\n");
err:
    rt_free(fil);
    rt_free(inbuff);
    return 1;
}

大概流程:
1 - 从SDIO读入JPEG文件信息
2 - 识别文件长度,申请对应大小的buffer
3 - 文件一次性读入buffer
4 - jpeg解码初始化,识别文件头,LCD显示区域初始化,开始解码
5 - 图片宽度width,高度height,每次解码一行数据(RGB888 * width),解码一行,显示屏显示一行,直至全部行(height)全部解码结束
6 - 释放相关资源

三、测试代码

#include "rtthread.h"
#include "board.h"

int jpeg_read_file(char * filename);

static char jpg_file[32] = "0:/jpeg/100.jpg";

static void test_entry(void* parameter)
{ 
    uint8_t i = 0;;

    while (1)
    {
        jpg_file[10] = i + 0x30;
        jpeg_read_file(jpg_file);
        rt_thread_delay(1000);
        i++;
        if (i > 4){i = 0;}
    }
}

static int test_init(void)
{
	rt_thread_t th = rt_thread_create("test", test_entry, RT_NULL, 2048, 10, 50);
	if (th != RT_NULL)rt_thread_startup(th);
	return 0;
}

INIT_APP_EXPORT(test_init);

TF卡存储5张图片100.jpg ~ 104.jpg,每隔1秒解码显示1张图片

HC32F460移植JPEG解码库

总结

实际效果还算行,感觉显示有些慢,主要是解码一行显示一行,如果display这边做多个显存缓冲,解码的时候,lcd dma传输缓冲数据,显示效果肯定还要好很多,这是以后优化的事情了。。。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值