MCU平臺libjpeg9移植使用說明

MCU平臺libjpeg9移植使用說明

隨着MCU平臺的性能越來越強大,在MCU上使用jpeg軟解碼也成爲可能,本文就將libjpeg移植做以說明。

libjpeg 是一個廣泛使用的 JPEG 圖像壓縮/解壓開源庫,採用 C 語言開發。因此可以方便的移植到各種平臺上。可以在這樣下載源碼 http://www.ijg.org/files/

這裏我們使用最新的libjpeg9版本,早先的libjpeg6版本中僅提供的接口都是從文件讀取源數據,而我們希望的是從內存中輸入源數據,在libjpeg9版本中新增了jpeg_mem_src可以實現我們的需求。

在PC上編寫測試代碼驗證

首先,第一步我們先不着急移植到開發板上,先在PC上編寫代碼測試下效果,這裏我使用主機開發環境是ubuntu18.04。本着方便期間可以直接使用apt安裝libjpeg9-dev這個包來使用。

sudo apt install libjpeg9-dev

當然,你可以自己編譯源碼

cd jpeg-9
./configure
make -j8

完成編譯後,在.libs目錄內可以找到libjpeg動態庫和靜態庫,也可以使用make install 安裝到本地目錄,這樣就和使用apt直接安裝一樣了。

接下來編寫一個測試代碼

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "jpeglib.h"

unsigned char jpeg_data[34518] = {
	0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 
	0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x06, 0x04, 0x05, 0x06, 0x05, 0x04, 0x06, 
	0x06, 0x05, 0x06, 0x07, 0x07, 0x06, 0x08, 0x0A, 0x10, 0x0A, 0x0A, 0x09, 0x09, 0x0A, 0x14, 0x0E, 
	這個數組內是一張jpeg的原始數據,這裏省略不寫...
};

#define PRINTF_JPEGDECODER_OUT_DATA          1   //開啓後將解碼數據打印在串口log內(YV12plan數據按16行分別打印YUV三個分量)
#define JPEGDECODER_OUT_DATA_TO_RAM        0   //開啓後將解碼數據輸出到內存中固定地址0x70000000
#define JPEGDECODER_OUT_COLOR_SPACE_YV12     0   //YV12格式:這種是jpeg解碼後的原始YV12plan數據,不進行任何色彩空間轉換,不增加解碼時間開銷
#define JPEGDECODER_OUT_COLOR_SPACE_YUV444   0   //RGB格式:這種是進行色彩空間轉換後的YUV444pack數據,增加解碼時間開銷
#define JPEGDECODER_OUT_COLOR_SPACE_RGB      1   //YVU444格式:這種是進行色彩空間轉換後的RGB888數據,增加解碼時間開銷

struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;

int jpegDecode(void)
{
    cinfo.err = jpeg_std_error(&jerr); 

    jpeg_create_decompress(&cinfo);
    jpeg_mem_src(&cinfo, (void *)jpeg_data, sizeof(jpeg_data));
    jpeg_read_header(&cinfo, TRUE);
    

#if JPEGDECODER_OUT_COLOR_SPACE_RGB 
    cinfo.out_color_space = JCS_RGB;
#endif
#if JPEGDECODER_OUT_COLOR_SPACE_YUV444
    cinfo.out_color_space = JCS_YCbCr;
#endif
#if JPEGDECODER_OUT_COLOR_SPACE_YV12
    cinfo.out_color_space = JCS_YCbCr;
    cinfo.raw_data_out = TRUE;
    cinfo.do_fancy_upsampling = FALSE;
#endif 
    
  	jpeg_start_decompress(&cinfo);
    
	switch (cinfo.out_color_space)
    {
        case JCS_RGB:
        {            
            JSAMPARRAY out_buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW));
            out_buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * cinfo.image_width * 3);
            
            #if JPEGDECODER_OUT_DATA_TO_RAM
            unsigned char *ram_out_buff = (unsigned char *)0x70000000;
            while (cinfo.output_scanline < cinfo.output_height) {
                jpeg_read_scanlines(&cinfo, out_buffer, 1);
                memcpy(ram_out_buff,out_buffer[0],cinfo.image_width * 3);
                ram_out_buff += cinfo.image_width * 3;
            }
            #else
            while (cinfo.output_scanline < cinfo.output_height) {
                jpeg_read_scanlines(&cinfo, out_buffer, 1);
                #if PRINTF_JPEGDECODER_OUT_DATA
                unsigned char *rgb_ptr = (unsigned char *)(out_buffer[0]);
                for (unsigned int i = 0; i < cinfo.output_width; i++) {       
                    printf("0x%02x ", *(rgb_ptr++));
                    printf("0x%02x ", *(rgb_ptr++));
                    printf("0x%02x ", *(rgb_ptr++));
                }
                #endif
            }
            #endif
            
            free(out_buffer[0]);
            free(out_buffer);
            break;
        }
        case JCS_YCbCr:
        {
            if(cinfo.raw_data_out == TRUE)
            {
                JSAMPIMAGE out_buffer = (JSAMPIMAGE)malloc(sizeof(JSAMPIMAGE) * 3);
                out_buffer[0] = (JSAMPARRAY  )malloc(sizeof(JSAMPARRAY ) * cinfo.output_height);
                out_buffer[1] = (JSAMPARRAY )malloc(sizeof(JSAMPARRAY ) * cinfo.output_height);
                out_buffer[2] = (JSAMPARRAY )malloc(sizeof(JSAMPARRAY ) * cinfo.output_height);
                JSAMPARRAY temp_out_buffer_0 = out_buffer[0];
                JSAMPARRAY temp_out_buffer_1 = out_buffer[1];
                JSAMPARRAY temp_out_buffer_2 = out_buffer[2];
                
                #if JPEGDECODER_OUT_DATA_TO_RAM
                //輸出數據到ram,驗證正確性
                unsigned char *ram_out_buff = (unsigned char *)0x70000000;
                int i;
                unsigned char *line;
                line = ram_out_buff;
                for (i = 0; i < cinfo.image_height; i ++, line += cinfo.output_width)
                    out_buffer[0][i] = line;
                if (2 == cinfo.comp_info[0].v_samp_factor)
                {
                    line = ram_out_buff + cinfo.output_width * cinfo.output_height;
                    for (i = 0; i < cinfo.image_height; i += 2, line += cinfo.output_width / 2)
                        out_buffer[1][i / 2] = line;

                    line = ram_out_buff + cinfo.output_width * cinfo.output_height * 5 / 4;
                    for (i = 0; i < cinfo.image_height; i += 2, line += cinfo.output_width / 2)
                        out_buffer[2][i / 2] =  line;

                    int y_read_line = 8;
                    for (int i = 0; cinfo.output_scanline < cinfo.output_height; i += 2 * y_read_line)
                    {
                        int j = jpeg_read_raw_data(&cinfo, out_buffer, 2 * y_read_line);
                        out_buffer[0] += 2 * y_read_line;
                        out_buffer[1] += y_read_line;
                        out_buffer[2] += y_read_line;
                    }
                }
                else
                {
                    printf("v_samp_factor is %d\n",cinfo.comp_info[0].v_samp_factor);
                    printf("is not YUV420 data\n");
					while(1);
                }
                #else
                // 不輸出數據,用於測試時間
                unsigned char *sram_out_buff = (unsigned char * )malloc(16*cinfo.output_width*3/2);
                int i;
                unsigned char *line;
                line = sram_out_buff;
                for (i = 0; i < 16; i ++, line += cinfo.output_width)
                    out_buffer[0][i] = line;
                if (2 == cinfo.comp_info[0].v_samp_factor)
                {
                    line = sram_out_buff + cinfo.output_width * 16;
                    for (i = 0; i < 16; i += 2, line += cinfo.output_width / 2)
                        out_buffer[1][i / 2] = line;

                    line = sram_out_buff + cinfo.output_width * 16 * 5 / 4;
                    for (i = 0; i < 16; i += 2, line += cinfo.output_width / 2)
                        out_buffer[2][i / 2] =  line;

                    int y_read_line = 8;
                    for (int i = 0; cinfo.output_scanline < cinfo.output_height; i += 2 * y_read_line)
                    {
                        jpeg_read_raw_data(&cinfo, out_buffer, 2 * y_read_line);
                        #if PRINTF_JPEGDECODER_OUT_DATA
                        printf("Y: ");
                        for (size_t j = 0; j < 2*y_read_line*cinfo.output_width; j++)
                        {
                            printf("0x%02X ", sram_out_buff[j]);
                        }
                        printf("\n");
                        printf("U: ");
                        for (size_t j = 0; j < y_read_line*cinfo.output_width; j++)
                        {
                            printf("0x%02X ", sram_out_buff[j+2*y_read_line*cinfo.output_width]);
                        }
                        printf("\n");
                        printf("V: ");
                        for (size_t j = 0; j < y_read_line*cinfo.output_width; j++)
                        {
                            printf("0x%02X ", sram_out_buff[j+3*y_read_line*cinfo.output_width]);
                        }
                        printf("\n");
                        printf("\n");
                        #endif
                    }
                }
                else
                {
                    printf("v_samp_factor is %d\n",cinfo.comp_info[0].v_samp_factor);
                    printf("is not YUV420 data\n");
					while(1);
                }
                free(sram_out_buff);
                #endif
                free(temp_out_buffer_0);
                free(temp_out_buffer_1);
                free(temp_out_buffer_2);
                free(out_buffer);
            }
            else
            {
                // jpeg_read_scanlines這種方式輸出的YUV444的pack數據,不是原始數據YUV420的plan數據
                JSAMPARRAY out_buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW));
                out_buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * cinfo.image_width * 3 );

                #if JPEGDECODER_OUT_DATA_TO_RAM
                unsigned char *ram_out_buff = (unsigned char *)0x70000000;
                while (cinfo.output_scanline < cinfo.output_height) {
                    jpeg_read_scanlines(&cinfo, out_buffer, 1);
                    memcpy(ram_out_buff,out_buffer[0],cinfo.image_width * 3);
                    ram_out_buff += cinfo.image_width * 3;
                }
                #else
                while (cinfo.output_scanline < cinfo.output_height) {
                    jpeg_read_scanlines(&cinfo, out_buffer, 1);
                    #if PRINTF_JPEGDECODER_OUT_DATA
                    unsigned char *yuv_ptr = (unsigned char *)(out_buffer[0]);
                    for (unsigned int i = 0; i < cinfo.output_width; i++) {
                        printf("0x%02x ", *(yuv_ptr++));
                        printf("0x%02x ", *(yuv_ptr++));
                        printf("0x%02x ", *(yuv_ptr++));
                    }
                    #endif
                }
                #endif
                
                free(out_buffer[0]);
                free(out_buffer);
            }
            break;
        }
        default:
            break;
	}

	jpeg_finish_decompress(&cinfo);
    
    jpeg_destroy_decompress(&cinfo);
    
    printf("\nfinsh\n");
    
    return 0;
}

int main()
{
	jpegDecode();
	return 0;
}

編譯上述代碼運行,可以確認解碼正常

gcc decode_jpeg.c -ljpeg -o decode_jpeg

這裏多說兩句,上述測試代碼中有以下三個測試宏,可以將jpeg的解碼結果輸出成不同格式的,實際驗證解碼成不同格式的數據,時間開銷也不同,後續我們移植到自己平臺上使用什麼樣的最終數據格式能節約MCU的帶寬是一個重要的考慮因素,因爲jpeg編碼是通過將rgb數據轉換問YUV的色彩空間進行壓縮的,而部分MCU平臺硬件提供了YUV轉換RGB的硬件加速單元,因此軟件解碼jpeg可以僅僅轉換到YV12格式,大大節約CPU帶寬資源。

另外代碼中的YV12格式的數據組織僅作爲示例,你可以根據自己需求的數據格式組織數據輸出,我之前在網上搜索了很多博客都沒找到這個庫解碼成YUV420格式的示例代碼,或者是有些代碼不能用,有些代碼過於老舊不適合新版本的libjpeg9。這份代碼可以給大家一個參考。

#define JPEGDECODER_OUT_COLOR_SPACE_YV12     0   //YV12格式:這種是jpeg解碼後的原始YV12plan數據,不進行任何色彩空間轉換,不增加解碼時間開銷
#define JPEGDECODER_OUT_COLOR_SPACE_YUV444   0   //RGB格式:這種是進行色彩空間轉換後的YUV444pack數據,增加解碼時間開銷
#define JPEGDECODER_OUT_COLOR_SPACE_RGB      1   //YVU444格式:這種是進行色彩空間轉換後的RGB888數據,增加解碼時間開銷

移植

接下來我們就可以將這個測試代碼已經libjpeg統統移植到mcu平臺上了。

添加以下文件到你MCU平臺工程內,注意libjpeg源碼內的其他文件不必添加你工程內,因爲有大量平臺相關的測試代碼,而你的平臺未必能支持到,以下源文件纔是該編解碼庫的核心功能。

jaricom.c、jcapimin.c、jcapistd.c、jccoefct.c、
jccolor.c、jcdctmgr.c、jchuff.c、jcinit.c、
jcmainct.c、jcmarker.c、jcmaster.c、jcomapi.c、
jcparam.c、jcprepct.c、jcsample.c、jctrans.c、
jdapimin.c、jdapistd.c、jdarith.c、jdatadst.c、
jdatasrc.c、jdcoefct.c、jdcolor.c、jddctmgr.c、
jdhuff.c、jdinput.c、jdmainct.c、jdmarker.c、
jdmaster.c、jdmerge.c、jdpostct.c、jdsample.c、
jdtrans.c、jerror.c、jfdctflt.c、jfdctfst.c、
jfdctint.c、jidctflt.c、jidctfst.c、jidctint.c、
jmemmgr.c、jmemnobs.c、jquant1.c、jquant2.c、 jutils.c

新建文件jconfig.h(PC上該文件有自動構建工具自動生成的,這裏我們要自己編寫適合自己平臺的,以下內容供參考),內容如下:

/*
 * jconfig.h
 *
 * Copyright (C) 1991-1994, Thomas G. Lane.
 * This file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README file.
 *
 * This file documents the configuration options that are required to
 * customize the JPEG software for a particular system.
 *
 * The actual configuration options for a particular installation are stored
 * in jconfig.h.  On many machines, jconfig.h can be generated automatically
 * or copied from one of the "canned" jconfig files that we supply.  But if
 * you need to generate a jconfig.h file by hand, this file tells you how.
 *
 * DO NOT EDIT THIS FILE --- IT WON'T ACCOMPLISH ANYTHING.
 * EDIT A COPY NAMED JCONFIG.H.
 */


/*
 * These symbols indicate the properties of your machine or compiler.
 * #define the symbol if yes, #undef it if no.
 */

/* Does your compiler support function prototypes?
 * (If not, you also need to use ansi2knr, see install.txt)
 */
#define HAVE_PROTOTYPES

/* Does your compiler support the declaration "unsigned char" ?
 * How about "unsigned short" ?
 */
#define HAVE_UNSIGNED_CHAR
#define HAVE_UNSIGNED_SHORT

/* Define "void" as "char" if your compiler doesn't know about type void.
 * NOTE: be sure to define void such that "void *" represents the most general
 * pointer type, e.g., that returned by malloc().
 */
#undef void

/* Define "const" as empty if your compiler doesn't know the "const" keyword.
 */
#undef const

/* Define this if an ordinary "char" type is unsigned.
 * If you're not sure, leaving it undefined will work at some cost in speed.
 * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal.
 */
#define CHAR_IS_UNSIGNED

/* Define this if your system has an ANSI-conforming <stddef.h> file.
 */
#define HAVE_STDDEF_H

/* Define this if your system has an ANSI-conforming <stdlib.h> file.
 */
#define HAVE_STDLIB_H 
#define FILE void  
#define USE_LIBJPEG_MY_LOG 
#define USE_LIBJPEG_MY_MALLOC    

/* Define this if your system does not have an ANSI/SysV <string.h>,
 * but does have a BSD-style <strings.h>.
 */
#undef NEED_BSD_STRINGS

/* Define this if your system does not provide typedef size_t in any of the
 * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in
 * <sys/types.h> instead.
 */
#undef NEED_SYS_TYPES_H

/* For 80x86 machines, you need to define NEED_FAR_POINTERS,
 * unless you are using a large-data memory model or 80386 flat-memory mode.
 * On less brain-damaged CPUs this symbol must not be defined.
 * (Defining this symbol causes large data structures to be referenced through
 * "far" pointers and to be allocated with a special version of malloc.)
 */
#undef NEED_FAR_POINTERS

/* Define this if your linker needs global names to be unique in less
 * than the first 15 characters.
 */
#undef NEED_SHORT_EXTERNAL_NAMES

/* Although a real ANSI C compiler can deal perfectly well with pointers to
 * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI
 * and pseudo-ANSI compilers get confused.  To keep one of these bozos happy,
 * define INCOMPLETE_TYPES_BROKEN.  This is not recommended unless you
 * actually get "missing structure definition" warnings or errors while
 * compiling the JPEG code.
 */
#undef INCOMPLETE_TYPES_BROKEN

/* Define "boolean" as unsigned char, not int, on Windows systems.
 */
#ifdef _WIN32
#ifndef __RPCNDR_H__		/* don't conflict if rpcndr.h already read */
typedef unsigned char boolean;
#endif
#define HAVE_BOOLEAN		/* prevent jmorecfg.h from redefining it */
#endif

/*
 * The following options affect code selection within the JPEG library,
 * but they don't need to be visible to applications using the library.
 * To minimize application namespace pollution, the symbols won't be
 * defined unless JPEG_INTERNALS has been defined.
 */

#ifdef JPEG_INTERNALS

/* Define this if your compiler implements ">>" on signed values as a logical
 * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift,
 * which is the normal and rational definition.
 */
#undef RIGHT_SHIFT_IS_UNSIGNED
#define INLINE inline
/* These are for configuring the JPEG memory manager. */
#undef DEFAULT_MAX_MEM
#undef NO_MKTEMP

#endif /* JPEG_INTERNALS */


/*
 * The remaining options do not affect the JPEG library proper,
 * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c).
 * Other applications can ignore these.
 */

#ifdef JPEG_CJPEG_DJPEG

/* These defines indicate which image (non-JPEG) file formats are allowed. */

#define BMP_SUPPORTED		/* BMP image file format */
#define GIF_SUPPORTED		/* GIF image file format */
#define PPM_SUPPORTED		/* PBMPLUS PPM/PGM image file format */
#undef RLE_SUPPORTED		/* Utah RLE image file format */
#define TARGA_SUPPORTED		/* Targa image file format */

/* Define this if you want to name both input and output files on the command
 * line, rather than using stdout and optionally stdin.  You MUST do this if
 * your system can't cope with binary I/O to stdin/stdout.  See comments at
 * head of cjpeg.c or djpeg.c.
 */
#undef TWO_FILE_COMMANDLINE

/* Define this if your system needs explicit cleanup of temporary files.
 * This is crucial under MS-DOS, where the temporary "files" may be areas
 * of extended memory; on most other systems it's not as important.
 */
#undef NEED_SIGNAL_CATCHER

/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb").
 * This is necessary on systems that distinguish text files from binary files,
 * and is harmless on most systems that don't.  If you have one of the rare
 * systems that complains about the "b" spec, define this symbol.
 */
#undef DONT_USE_B_MODE

/* Define this if you want percent-done progress reports from cjpeg/djpeg.
 */
#undef PROGRESS_REPORT


#endif /* JPEG_CJPEG_DJPEG */

修改以下兩個源文件,注意對比以下相比原本改動的內容是輸出printf打印和malloc內存分配相關的內容,你可以根據我的修改成適合你平臺的接口

jmemnobs.c


/*
 * jmemnobs.c
 *
 * Copyright (C) 1992-1996, Thomas G. Lane.
 * This file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README file.
 *
 * This file provides a really simple implementation of the system-
 * dependent portion of the JPEG memory manager.  This implementation
 * assumes that no backing-store files are needed: all required space
 * can be obtained from malloc().
 * This is very portable in the sense that it'll compile on almost anything,
 * but you'd better have lots of main memory (or virtual memory) if you want
 * to process big images.
 * Note that the max_memory_to_use option is ignored by this implementation.
 */

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

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

#ifdef USE_LIBJPEG_MY_MALLOC
#include "my_log.h"
extern void *pvPortCalloc( size_t nmemb, size_t size );
extern void *pvPortMalloc( size_t xWantedSize );
extern void *pvPortRealloc( void *pv, size_t size );
extern void vPortFree( void *pv );

#define malloc  pvPortMalloc 
#define calloc  pvPortCalloc 
#define realloc pvPortRealloc 
#define free    vPortFree
#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)
{
  void *p = (void *) malloc(sizeofobject);
  if(p == NULL) {
        get_fmem_status();
        mprintf("sizeofobject %d\n",sizeofobject);
  }
  return p;
}

GLOBAL(void)
jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
{
  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)
{
  void *p = (void *) malloc(sizeofobject);
  if(p == NULL) {
        get_fmem_status();
        mprintf("sizeofobject %d\n",sizeofobject);
  }
  return (void FAR *)p;
}

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


/*
 * This routine computes the total memory space available for allocation.
 * Here we always say, "we got all you want bud!"
 */

GLOBAL(long)
jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed,
		    long max_bytes_needed, long already_allocated)
{
  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 */
}

jerror.c

/*
 * jerror.c
 *
 * Copyright (C) 1991-1998, Thomas G. Lane.
 * Modified 2012 by Guido Vollbeding.
 * This file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README file.
 *
 * This file contains simple error-reporting and trace-message routines.
 * These are suitable for Unix-like systems and others where writing to
 * stderr is the right thing to do.  Many applications will want to replace
 * some or all of these routines.
 *
 * If you define USE_WINDOWS_MESSAGEBOX in jconfig.h or in the makefile,
 * you get a Windows-specific hack to display error messages in a dialog box.
 * It ain't much, but it beats dropping error messages into the bit bucket,
 * which is what happens to output to stderr under most Windows C compilers.
 *
 * These routines are used by both the compression and decompression code.
 */

/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
#include "jinclude.h"
#include "jpeglib.h"
#include "jversion.h"
#include "jerror.h"

#ifdef USE_WINDOWS_MESSAGEBOX
#include <windows.h>
#endif

#ifdef USE_LIBJPEG_MY_LOG
#include "my_log.h"
#endif

#ifndef EXIT_FAILURE		/* define exit() codes if not provided */
#define EXIT_FAILURE  1
#endif


/*
 * Create the message string table.
 * We do this from the master message list in jerror.h by re-reading
 * jerror.h with a suitable definition for macro JMESSAGE.
 * The message table is made an external symbol just in case any applications
 * want to refer to it directly.
 */

#ifdef NEED_SHORT_EXTERNAL_NAMES
#define jpeg_std_message_table	jMsgTable
#endif

#define JMESSAGE(code,string)	string ,

const char * const jpeg_std_message_table[] = {
#include "jerror.h"
  NULL
};


/*
 * Error exit handler: must not return to caller.
 *
 * Applications may override this if they want to get control back after
 * an error.  Typically one would longjmp somewhere instead of exiting.
 * The setjmp buffer can be made a private field within an expanded error
 * handler object.  Note that the info needed to generate an error message
 * is stored in the error object, so you can generate the message now or
 * later, at your convenience.
 * You should make sure that the JPEG object is cleaned up (with jpeg_abort
 * or jpeg_destroy) at some point.
 */

METHODDEF(noreturn_t)
error_exit (j_common_ptr cinfo)
{
  /* Always display the message */
  (*cinfo->err->output_message) (cinfo);

  /* Let the memory manager delete any temp files before we die */
  jpeg_destroy(cinfo);

  exit(EXIT_FAILURE);
}


/*
 * Actual output of an error or trace message.
 * Applications may override this method to send JPEG messages somewhere
 * other than stderr.
 *
 * On Windows, printing to stderr is generally completely useless,
 * so we provide optional code to produce an error-dialog popup.
 * Most Windows applications will still prefer to override this routine,
 * but if they don't, it'll do something at least marginally useful.
 *
 * NOTE: to use the library in an environment that doesn't support the
 * C stdio library, you may have to delete the call to fprintf() entirely,
 * not just not use this routine.
 */

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 */
  #ifdef USE_LIBJPEG_MY_LOG
  mprintf("%s\n", buffer);
  #else
  fprintf(stderr, "%s\n", buffer);
  #endif
#endif
}


/*
 * Decide whether to emit a trace or warning message.
 * msg_level is one of:
 *   -1: recoverable corrupt-data warning, may want to abort.
 *    0: important advisory messages (always display to user).
 *    1: first level of tracing detail.
 *    2,3,...: successively more detailed tracing messages.
 * An application might override this method if it wanted to abort on warnings
 * or change the policy about which messages to display.
 */

METHODDEF(void)
emit_message (j_common_ptr cinfo, int msg_level)
{
  struct jpeg_error_mgr * err = cinfo->err;

  if (msg_level < 0) {
    /* It's a warning message.  Since corrupt files may generate many warnings,
     * the policy implemented here is to show only the first warning,
     * unless trace_level >= 3.
     */
    if (err->num_warnings == 0 || err->trace_level >= 3)
      (*err->output_message) (cinfo);
    /* Always count warnings in num_warnings. */
    err->num_warnings++;
  } else {
    /* It's a trace message.  Show it if trace_level >= msg_level. */
    if (err->trace_level >= msg_level)
      (*err->output_message) (cinfo);
  }
}


/*
 * Format a message string for the most recent JPEG error or message.
 * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX
 * characters.  Note that no '\n' character is added to the string.
 * Few applications should need to override this method.
 */

METHODDEF(void)
format_message (j_common_ptr cinfo, char * buffer)
{
  struct jpeg_error_mgr * err = cinfo->err;
  int msg_code = err->msg_code;
  const char * msgtext = NULL;
  const char * msgptr;
  char ch;
  boolean isstring;

  /* Look up message string in proper table */
  if (msg_code > 0 && msg_code <= err->last_jpeg_message) {
    msgtext = err->jpeg_message_table[msg_code];
  } else if (err->addon_message_table != NULL &&
	     msg_code >= err->first_addon_message &&
	     msg_code <= err->last_addon_message) {
    msgtext = err->addon_message_table[msg_code - err->first_addon_message];
  }

  /* Defend against bogus message number */
  if (msgtext == NULL) {
    err->msg_parm.i[0] = msg_code;
    msgtext = err->jpeg_message_table[0];
  }

  /* Check for string parameter, as indicated by %s in the message text */
  isstring = FALSE;
  msgptr = msgtext;
  while ((ch = *msgptr++) != '\0') {
    if (ch == '%') {
      if (*msgptr == 's') isstring = TRUE;
      break;
    }
  }

  /* Format the message into the passed buffer */
  if (isstring)
    sprintf(buffer, msgtext, err->msg_parm.s);
  else
    sprintf(buffer, msgtext,
	    err->msg_parm.i[0], err->msg_parm.i[1],
	    err->msg_parm.i[2], err->msg_parm.i[3],
	    err->msg_parm.i[4], err->msg_parm.i[5],
	    err->msg_parm.i[6], err->msg_parm.i[7]);
}


/*
 * Reset error state variables at start of a new image.
 * This is called during compression startup to reset trace/error
 * processing to default state, without losing any application-specific
 * method pointers.  An application might possibly want to override
 * this method if it has additional error processing state.
 */

METHODDEF(void)
reset_error_mgr (j_common_ptr cinfo)
{
  cinfo->err->num_warnings = 0;
  /* trace_level is not reset since it is an application-supplied parameter */
  cinfo->err->msg_code = 0;	/* may be useful as a flag for "no error" */
}


/*
 * Fill in the standard error-handling methods in a jpeg_error_mgr object.
 * Typical call is:
 *	struct jpeg_compress_struct cinfo;
 *	struct jpeg_error_mgr err;
 *
 *	cinfo.err = jpeg_std_error(&err);
 * after which the application may override some of the methods.
 */

GLOBAL(struct jpeg_error_mgr *)
jpeg_std_error (struct jpeg_error_mgr * err)
{
  err->error_exit = error_exit;
  err->emit_message = emit_message;
  err->output_message = output_message;
  err->format_message = format_message;
  err->reset_error_mgr = reset_error_mgr;

  err->trace_level = 0;		/* default = no tracing */
  err->num_warnings = 0;	/* no warnings emitted yet */
  err->msg_code = 0;		/* may be useful as a flag for "no error" */

  /* Initialize message table pointers */
  err->jpeg_message_table = jpeg_std_message_table;
  err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1;

  err->addon_message_table = NULL;
  err->first_addon_message = 0;	/* for safety */
  err->last_addon_message = 0;

  return err;
}

最後,添加我們之前的測試源碼就可以對比PC輸出結果,驗證移植的正確性了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章