Fawkes API  Fawkes Development Version
jpeg_compressor.cpp
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