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输出结果,验证移植的正确性了。