MCU platform libjpeg9 porting instructions
With the performance of MCU platform becoming more and more powerful, it is possible to use jpeg soft decoding on MCU. This paper will explain the migration of libjpeg.
libjpeg is a widely used JPEG image compression / decompression open source library, which is developed in C language. Therefore, it can be easily transplanted to various platforms. You can download the source code here http://www.ijg.org/files/
Here we use the latest version of libjpeg 9. In the previous version of libjpeg 6, only the interfaces provided were to read the source data from the file. What we wanted was to input the source data from the memory. In the libjpeg 9 version, we added jpeg_mem_src can meet our needs.
Write test code on PC for verification
First of all, we don't rush to migrate to the development board. First, we write code on the PC to test the effect. Here I use Ubuntu 18.04 as the host development environment. During the convenience period, you can use apt to install libjpeg 9-dev directly.
sudo apt install libjpeg9-dev
Of course, you can compile the source code yourself
cd jpeg-9 ./configure make -j8
After the compilation, you can find libjpeg dynamic and static libraries in the. libs directory, or you can use make install to install to the local directory, which is the same as using apt to install directly.
Next, write a test code
#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, //This array is a piece of jpeg raw data, which is omitted here... }; #define PRINTF_JPEGDECODER_OUT_DATA 1 //After opening, the decoded data will be printed in the serial port log (YV12plan data will be printed with three YUV components in 16 lines) #define JPEGDECODER_OUT_DATA_TO_RAM 0 //Output decoded data to fixed address 0x70000000 in memory after opening #define JPEGDECODER_OUT_COLOR_SPACE_YV12 0 //YV12 format: This is the original YV12 plan data decoded by jpeg, without any color space conversion and increasing decoding time overhead #define JPEGDECODER_OUT_COLOR_SPACE_YUV444 0 //RGB format: This is YUV444pack data after color space conversion, which increases decoding time overhead #define JPEGDECODER_OUT_COLOR_SPACE_RGB 1 //YVU444 format: This is RGB888 data after color space conversion, increasing decoding time overhead 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 //Output data to ram to verify correctness 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 // Do not output data for test time 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_ The pack age data of YUV444 output by scanlines is not the plan data of YUV420 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; }
Compile the above code to run, and confirm that the decoding is normal
gcc decode_jpeg.c -ljpeg -o decode_jpeg
Here are two more sentences. There are three test macros in the above test code, which can output the decoding results of jpeg into different formats. The actual verification shows that the decoding results into different formats have different time costs. What kind of final data format can be used to save the bandwidth of MCU when we migrate to our own platform later is an important consideration, because jpeg coding is based on RG The color space of YUV is compressed in B data conversion, and some MCU platform hardware provides hardware acceleration unit of YUV conversion RGB, so software decoding jpeg can only convert to YV12 format, greatly saving CPU bandwidth resources.
In addition, the data organization of YV12 format in the code is only used as an example. You can organize the data output according to the data format you need. I searched many blogs on the Internet before, but I didn't find the example code decoded into YUV420 format by this library, or some codes can't be used, some codes are too old and not suitable for the new version of libjpeg 9. This code can give you a reference.
#define JPEGDECODER_ OUT_ COLOR_ SPACE_ Yv12 0 / / yv12 format: This is the original yv12 plan data decoded by jpeg, without any color space conversion and increasing decoding time overhead #define JPEGDECODER_ OUT_ COLOR_ SPACE_ Yuv444 0 / / RGB format: This is the YUV444pack data after color space conversion, which increases the decoding time overhead #define JPEGDECODER_ OUT_ COLOR_ SPACE_ RGB 1 / / yvu444 format: This is RGB888 data after color space conversion, increasing decoding time overhead
transplant
Next, we can port the test code libjpeg to the mcu platform.
Add the following files to your MCU platform project. Note that other files in libjpeg source code do not need to be added to your project, because there are a lot of platform related test code, and your platform may not be able to support it. The following source files are the core functions of this codec library.
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
Create a new file jconfig. H (this file is automatically generated by the auto build tool on the PC, here we need to write a file that is suitable for our platform, and the following content is for reference). The content is as follows:
/* * 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 */
Modify the following two source files, and pay attention to the following comparison. The original modified content is the output of printf printing and malloc memory allocation. You can change it into an interface suitable for your platform according to my modification
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; }
Finally, by adding our previous test source code, we can compare the PC output results to verify the correctness of the migration.