From 9b6b7dbefa8fb53300c31edef36d9c3546b96449 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 25 Dec 2014 16:35:37 +0100 Subject: [PATCH] JPEG output: enable optimized coding Add OPTIMIZED=YES/NO/ARITHMETIC (default YES) format option to turn on optimized Huffman coding. If set to NO, use standard Huffman tables (a little bit faster in theory, but less efficient for output size). If set to ARITHMETIC, will use Arithmetic entropy coding instead of Huffman, for smaller size. Note that Arithmetic coding/decoding is available in libjpeg-turbo implementation, but not in IJG libjpeg-6b or later. So must only be used in combinations with clients that are known to be able to decode Arithmetic encoded JPEG. --- mapimageio.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/mapimageio.c b/mapimageio.c index a9268f329e..e6bdcb5b6f 100644 --- a/mapimageio.c +++ b/mapimageio.c @@ -127,17 +127,50 @@ int jpeg_buffer_empty_output_buffer (j_compress_ptr cinfo) return TRUE; } +static void msJPEGErrorExit(j_common_ptr cinfo) +{ + jmp_buf* pJmpBuffer = (jmp_buf* ) cinfo->client_data; + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + msSetError(MS_MISCERR,"libjpeg: %s","jpeg_ErrorExit()", buffer); -int saveAsJPEG(mapObj *map /*not used*/, rasterBufferObj *rb, streamInfo *info, + /* Return control to the setjmp point */ + longjmp(*pJmpBuffer, 1); +} + +int saveAsJPEG(mapObj *map, rasterBufferObj *rb, streamInfo *info, outputFormatObj *format) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; - int quality = atoi(msGetOutputFormatOption( format, "QUALITY", "75")); + int quality; + const char* pszOptimized; + int optimized; + int arithmetic; ms_destination_mgr *dest; - JSAMPLE *rowdata; + JSAMPLE *rowdata = NULL; unsigned int row; + jmp_buf setjmp_buffer; + + quality = atoi(msGetOutputFormatOption( format, "QUALITY", "75")); + pszOptimized = msGetOutputFormatOption( format, "OPTIMIZED", "YES"); + optimized = EQUAL(pszOptimized, "YES") || EQUAL(pszOptimized, "ON") || + EQUAL(pszOptimized, "TRUE"); + arithmetic = EQUAL(pszOptimized, "ARITHMETIC"); + + if (setjmp(setjmp_buffer)) + { + jpeg_destroy_compress(&cinfo); + free(rowdata); + return MS_FAILURE; + } + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = msJPEGErrorExit; + cinfo.client_data = (void *) &(setjmp_buffer); jpeg_create_compress(&cinfo); if (cinfo.dest == NULL) { @@ -167,9 +200,23 @@ int saveAsJPEG(mapObj *map /*not used*/, rasterBufferObj *rb, streamInfo *info, cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); - jpeg_start_compress(&cinfo, TRUE); + if( arithmetic ) + cinfo.arith_code = TRUE; + else if( optimized ) + cinfo.optimize_coding = TRUE; + + if( arithmetic || optimized ) { + if (map == NULL || msGetConfigOption(map, "JPEGMEM") == NULL) { + /* If the user doesn't provide a value for JPEGMEM, we want to be sure */ + /* that at least the image size will be used before creating the temporary file */ + cinfo.mem->max_memory_to_use = + MAX(cinfo.mem->max_memory_to_use, cinfo.input_components * rb->width * rb->height); + } + } + jpeg_start_compress(&cinfo, TRUE); rowdata = (JSAMPLE*)malloc(rb->width*cinfo.input_components*sizeof(JSAMPLE)); + for(row=0; rowheight; row++) { JSAMPLE *pixptr = rowdata; int col;