Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * jpeg_compressor.cpp - JPEG image compressor 00004 * 00005 * Created: Sat Aug 12 13:42:39 2006 (in LFI of Central Medical Library 00006 * of Germany, Cologne) 00007 * Copyright 2005-2011 Tim Niemueller [www.niemueller.de] 00008 * 00009 ****************************************************************************/ 00010 00011 /* This program is free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. A runtime exception applies to 00015 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00016 * 00017 * This program is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU Library General Public License for more details. 00021 * 00022 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00023 */ 00024 00025 00026 #include <fvutils/compression/jpeg_compressor.h> 00027 #include <fvutils/color/yuvrgb.h> 00028 #include <fvutils/color/rgbyuv.h> 00029 00030 #include <core/exception.h> 00031 00032 #include <cstdio> 00033 #include <cstring> 00034 #include <cstdlib> 00035 #include <setjmp.h> 00036 extern "C" { 00037 #include <jpeglib.h> 00038 #include <jerror.h> 00039 } 00040 00041 namespace firevision { 00042 #if 0 /* just to make Emacs auto-indent happy */ 00043 } 00044 #endif 00045 00046 ///@cond INTERNALS 00047 00048 /** JPEG error manager type. */ 00049 typedef struct { 00050 struct jpeg_error_mgr pub; /**< manager */ 00051 jmp_buf setjmp_buffer; /**< jmp buffer */ 00052 } fv_jpeg_error_mgr_t; 00053 00054 00055 /** Defines a new destination manager to store images in memory 00056 * derived by jdatadst.c 00057 */ 00058 typedef struct { 00059 struct jpeg_destination_mgr pub; /**< public fields */ 00060 JOCTET *buffer; /**< start of buffer */ 00061 int bufsize; /**< buffer size */ 00062 int datacount; /**< final data size */ 00063 } fv_jpeg_memory_destination_mgr_t; 00064 00065 00066 /** Initialize destination 00067 * called by jpeg_start_compress before any data is actually written. 00068 * @param cinfo compression info 00069 */ 00070 METHODDEF(void) 00071 fv_jpeg_init_destination (j_compress_ptr cinfo) 00072 { 00073 fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest; 00074 dest->pub.next_output_byte = dest->buffer; 00075 dest->pub.free_in_buffer = dest->bufsize; 00076 dest->datacount=0; 00077 } 00078 00079 /** Empty the output buffer 00080 * called whenever buffer fills up. 00081 * @param cinfo compression info 00082 */ 00083 METHODDEF(boolean) 00084 fv_jpeg_empty_output_buffer (j_compress_ptr cinfo) 00085 { 00086 fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest; 00087 dest->pub.next_output_byte = dest->buffer; 00088 dest->pub.free_in_buffer = dest->bufsize; 00089 00090 return TRUE; 00091 } 00092 00093 /** Terminate destination 00094 * called by jpeg_finish_compress after all data has been written. 00095 * Usually needs to flush buffer. 00096 * @param cinfo compression info 00097 */ 00098 METHODDEF(void) 00099 fv_jpeg_term_destination (j_compress_ptr cinfo) 00100 { 00101 /* expose the finale compressed image size */ 00102 00103 fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest; 00104 dest->datacount = dest->bufsize - dest->pub.free_in_buffer; 00105 } 00106 00107 /** Setup memory destination. 00108 * @param cinfo compression info 00109 * @param buffer buffer 00110 * @param bufsize buffer size 00111 */ 00112 GLOBAL(void) 00113 fv_jpeg_memory_destination_setup(j_compress_ptr cinfo, JOCTET *buffer,int bufsize) 00114 { 00115 fv_jpeg_memory_destination_mgr_t *dest; 00116 if ( cinfo->dest == NULL ) { 00117 cinfo->dest = (struct jpeg_destination_mgr *) 00118 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, 00119 sizeof(fv_jpeg_memory_destination_mgr_t)); 00120 00121 } 00122 00123 dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest; 00124 dest->bufsize = bufsize; 00125 dest->buffer = buffer; 00126 dest->pub.init_destination = fv_jpeg_init_destination; 00127 dest->pub.empty_output_buffer = fv_jpeg_empty_output_buffer; 00128 dest->pub.term_destination = fv_jpeg_term_destination; 00129 } 00130 00131 METHODDEF(void) 00132 init_source(j_decompress_ptr cinfo) 00133 { 00134 /* nothing to do */ 00135 } 00136 00137 METHODDEF(boolean) 00138 fill_input_buffer(j_decompress_ptr cinfo) 00139 { 00140 /* can't fill */ 00141 return FALSE; 00142 } 00143 00144 METHODDEF(void) 00145 skip_input_data(j_decompress_ptr cinfo, long num_bytes) 00146 { 00147 if ((size_t)num_bytes > cinfo->src->bytes_in_buffer) { 00148 cinfo->src->next_input_byte = NULL; 00149 cinfo->src->bytes_in_buffer = 0; 00150 } else { 00151 cinfo->src->next_input_byte += (size_t) num_bytes; 00152 cinfo->src->bytes_in_buffer -= (size_t) num_bytes; 00153 } 00154 } 00155 00156 METHODDEF(void) 00157 term_source(j_decompress_ptr cinfo) 00158 { 00159 /* nothing to do */ 00160 } 00161 00162 METHODDEF(void) 00163 fv_jpeg_error_exit(j_common_ptr cinfo) 00164 { 00165 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ 00166 fv_jpeg_error_mgr_t *myerr = (fv_jpeg_error_mgr_t *) cinfo->err; 00167 00168 /* Return control to the setjmp point */ 00169 longjmp(myerr->setjmp_buffer, 1); 00170 } 00171 00172 00173 /** 00174 * set momory-jpeg image to JPEG lib Info struct 00175 * @param cinfo JPEG lib decompress infomation structure 00176 * @param ptr JPEG image 00177 * @param size JPEG image size 00178 */ 00179 GLOBAL(void) 00180 fv_jpeg_memory_source_setup(j_decompress_ptr cinfo, unsigned char *ptr, size_t size) 00181 { 00182 struct jpeg_source_mgr *src; 00183 src = cinfo->src = (struct jpeg_source_mgr *) 00184 (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, 00185 JPOOL_PERMANENT, 00186 sizeof(*src)); 00187 src->init_source = init_source; 00188 src->fill_input_buffer = fill_input_buffer; 00189 src->skip_input_data = skip_input_data; 00190 src->resync_to_restart = jpeg_resync_to_restart; 00191 src->term_source = term_source; 00192 src->next_input_byte = ptr; 00193 src->bytes_in_buffer = size; 00194 } 00195 00196 /// @endcond 00197 00198 /** @class JpegImageCompressor <fvutils/compression/jpeg_compressor.h> 00199 * Jpeg image compressor. 00200 */ 00201 00202 00203 /** Constructor. 00204 * @param quality JPEG quality in percent 00205 * @param jcs Jpeg colorspace 00206 */ 00207 JpegImageCompressor::JpegImageCompressor(unsigned int quality, JpegColorspace jcs) 00208 { 00209 this->quality = quality; 00210 jpeg_cs = jcs; 00211 } 00212 00213 /** Destructor. */ 00214 JpegImageCompressor::~JpegImageCompressor() 00215 { 00216 } 00217 00218 00219 void 00220 JpegImageCompressor::compress() 00221 { 00222 struct jpeg_compress_struct cinfo; 00223 fv_jpeg_error_mgr_t jerr; 00224 unsigned int row_stride; 00225 unsigned char *row_buffer; 00226 00227 // mem destination specific 00228 fv_jpeg_memory_destination_mgr_t *dest; 00229 00230 // file destination specific 00231 FILE *outfile = NULL; 00232 00233 /* zero out the compression info structure and 00234 allocate a new compressor handle */ 00235 memset (&cinfo, 0, sizeof(cinfo)); 00236 cinfo.err = jpeg_std_error(&jerr.pub); 00237 jerr.pub.error_exit = fv_jpeg_error_exit; 00238 00239 /* Establish the setjmp return context for my_error_exit to use. */ 00240 if (setjmp(jerr.setjmp_buffer)) { 00241 char buffer[JMSG_LENGTH_MAX]; 00242 (*cinfo.err->format_message) ((jpeg_common_struct *)&cinfo, buffer); 00243 00244 /* If we get here, the JPEG code has signaled an error. 00245 * We need to clean up the JPEG object, close the input file, and return. 00246 */ 00247 jpeg_destroy_compress(&cinfo); 00248 throw fawkes::Exception("Compression failed: %s", buffer); 00249 } 00250 00251 jpeg_create_compress(&cinfo); 00252 00253 /* Setup JPEG datastructures */ 00254 cinfo.image_width = width; /* image width and height, in pixels */ 00255 cinfo.image_height = height; 00256 cinfo.input_components = 3; /* # of color components per pixel=3 RGB */ 00257 if ( jpeg_cs == JPEG_CS_RGB ) { 00258 cinfo.in_color_space = JCS_RGB; 00259 } else { 00260 cinfo.in_color_space = JCS_YCbCr; 00261 } 00262 00263 row_stride = cinfo.image_width * cinfo.input_components; 00264 00265 if ( compdest == COMP_DEST_MEM ) { 00266 // mem 00267 fv_jpeg_memory_destination_setup(&cinfo, (JOCTET *)jpeg_buffer, jpeg_buffer_size); 00268 } else { 00269 outfile = fopen(filename, "wb"); 00270 if (outfile == NULL) { 00271 throw fawkes::Exception("JpegImageCompressor: cannot open %s\n", filename); 00272 } 00273 jpeg_stdio_dest( &cinfo, outfile ); 00274 } 00275 /* Setup compression */ 00276 jpeg_set_defaults(&cinfo); 00277 jpeg_set_quality (&cinfo, quality, true /* limit to baseline-JPEG values */); 00278 jpeg_start_compress(&cinfo, true); 00279 00280 /* compress each scanline one-at-a-time */ 00281 row_buffer = (unsigned char *)malloc( row_stride ); 00282 00283 00284 if ( jpeg_cs == JPEG_CS_RGB ) { 00285 while (cinfo.next_scanline < cinfo.image_height) { 00286 convert_line_yuv422planar_to_rgb( buffer, row_buffer, 00287 cinfo.image_width, cinfo.image_height, 00288 cinfo.next_scanline, 0 ); 00289 jpeg_write_scanlines(&cinfo, &row_buffer, 1); 00290 } 00291 } else { 00292 while (cinfo.next_scanline < cinfo.image_height) { 00293 convert_line_yuv422planar_to_yuv444packed( buffer, row_buffer, 00294 cinfo.image_width, cinfo.image_height, 00295 cinfo.next_scanline, 0 ); 00296 jpeg_write_scanlines(&cinfo, &row_buffer, 1); 00297 } 00298 } 00299 00300 free(row_buffer); 00301 jpeg_finish_compress(&cinfo); 00302 00303 if ( compdest == COMP_DEST_MEM ) { 00304 /* Now extract the size of the compressed buffer */ 00305 dest=(fv_jpeg_memory_destination_mgr_t *)cinfo.dest; 00306 jpeg_bytes = dest->datacount; /* the actual compressed datasize */ 00307 } else { 00308 fclose( outfile ); 00309 } 00310 00311 /* destroy the compressor handle */ 00312 jpeg_destroy_compress(&cinfo); 00313 00314 } 00315 00316 00317 void 00318 JpegImageCompressor::set_image_dimensions(unsigned int width, unsigned int height) 00319 { 00320 this->width = width; 00321 this->height = height; 00322 } 00323 00324 00325 void 00326 JpegImageCompressor::set_image_buffer(colorspace_t cspace, unsigned char *buffer) 00327 { 00328 if ( cspace == YUV422_PLANAR ) { 00329 this->buffer = buffer; 00330 } 00331 } 00332 00333 00334 void 00335 JpegImageCompressor::set_compression_destination(ImageCompressor::CompressionDestination cd) 00336 { 00337 compdest = cd; 00338 } 00339 00340 00341 bool 00342 JpegImageCompressor::supports_compression_destination(ImageCompressor::CompressionDestination cd) 00343 { 00344 return true; 00345 } 00346 00347 00348 void 00349 JpegImageCompressor::set_destination_buffer(unsigned char *buf, unsigned int buf_size) 00350 { 00351 jpeg_buffer = buf; 00352 jpeg_buffer_size = buf_size; 00353 } 00354 00355 00356 size_t 00357 JpegImageCompressor::compressed_size() 00358 { 00359 return jpeg_bytes; 00360 } 00361 00362 size_t 00363 JpegImageCompressor::recommended_compressed_buffer_size() 00364 { 00365 return width * height / 4; 00366 } 00367 00368 00369 void 00370 JpegImageCompressor::set_filename(const char *filename) 00371 { 00372 this->filename = filename; 00373 } 00374 00375 } // end namespace firevision