jpeg_compressor.cpp

00001
00002 /***************************************************************************
00003  *  jpeg.cpp - JPEG image compressor
00004  *
00005  *  Generated: Sat Aug 12 13:42:39 2006 (in LFI of Central Medical Library
00006  *                                       of Germany, Cologne)
00007  *  Copyright  2005-2007  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 ///@cond INTERNALS
00042 
00043 /** JPEG error manager type. */
00044 typedef struct {
00045   struct jpeg_error_mgr pub;    /**< manager */
00046   jmp_buf setjmp_buffer;        /**< jmp buffer */
00047 } fv_jpeg_error_mgr_t;
00048
00049 
00050 /** Defines a new destination manager to store images in memory
00051  * derived by jdatadst.c
00052  */
00053 typedef struct {
00054   struct jpeg_destination_mgr pub;      /**< public fields */
00055   JOCTET *buffer;                       /**< start of buffer */
00056   int bufsize;                          /**< buffer size */
00057   int datacount;                        /**< final data size */
00058 } fv_jpeg_memory_destination_mgr_t;
00059
00060 
00061 /** Initialize destination
00062  * called by jpeg_start_compress before any data is actually written.
00063  * @param cinfo compression info
00064  */
00065 METHODDEF(void)
00066 fv_jpeg_init_destination (j_compress_ptr cinfo)
00067 {
00068   fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
00069   dest->pub.next_output_byte = dest->buffer;
00070   dest->pub.free_in_buffer = dest->bufsize;
00071   dest->datacount=0;
00072 }
00073 
00074 /** Empty the output buffer
00075  * called whenever buffer fills up.
00076  * @param cinfo compression info
00077  */
00078 METHODDEF(boolean)
00079 fv_jpeg_empty_output_buffer (j_compress_ptr cinfo)
00080 {
00081   fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
00082   dest->pub.next_output_byte = dest->buffer;
00083   dest->pub.free_in_buffer = dest->bufsize;
00084
00085   return TRUE;
00086 }
00087 
00088 /** Terminate destination
00089  * called by jpeg_finish_compress after all data has been written.
00090  * Usually needs to flush buffer.
00091  * @param cinfo compression info
00092  */
00093 METHODDEF(void)
00094 fv_jpeg_term_destination (j_compress_ptr cinfo)
00095 {
00096   /* expose the finale compressed image size */
00097
00098   fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
00099   dest->datacount = dest->bufsize - dest->pub.free_in_buffer;
00100 }
00101 
00102 /** Setup memory destination.
00103  * @param cinfo compression info
00104  * @param buffer buffer
00105  * @param bufsize buffer size
00106  */
00107 GLOBAL(void)
00108 fv_jpeg_memory_destination_setup(j_compress_ptr cinfo, JOCTET *buffer,int bufsize)
00109 {
00110   fv_jpeg_memory_destination_mgr_t *dest;
00111   if ( cinfo->dest == NULL ) {
00112     cinfo->dest = (struct jpeg_destination_mgr *)
00113       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
00114                                   sizeof(fv_jpeg_memory_destination_mgr_t));
00115
00116   }
00117
00118   dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
00119   dest->bufsize = bufsize;
00120   dest->buffer = buffer;
00121   dest->pub.init_destination = fv_jpeg_init_destination;
00122   dest->pub.empty_output_buffer = fv_jpeg_empty_output_buffer;
00123   dest->pub.term_destination = fv_jpeg_term_destination;
00124 }
00125
00126 METHODDEF(void)
00127 init_source(j_decompress_ptr cinfo)
00128 {
00129   /* nothing to do */
00130 }
00131
00132 METHODDEF(boolean)
00133 fill_input_buffer(j_decompress_ptr cinfo)
00134 {
00135   /* can't fill */
00136   return FALSE;
00137 }
00138
00139 METHODDEF(void)
00140 skip_input_data(j_decompress_ptr cinfo, long num_bytes)
00141 {
00142   if ((size_t)num_bytes > cinfo->src->bytes_in_buffer) {
00143     cinfo->src->next_input_byte = NULL;
00144     cinfo->src->bytes_in_buffer = 0;
00145   } else {
00146     cinfo->src->next_input_byte += (size_t) num_bytes;
00147     cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
00148   }
00149 }
00150
00151 METHODDEF(void)
00152 term_source(j_decompress_ptr cinfo)
00153 {
00154   /* nothing to do */
00155 }
00156
00157 
00158 /**
00159  * set momory-jpeg image to JPEG lib Info struct
00160  * @param cinfo  JPEG lib decompress infomation structure
00161  * @param ptr    JPEG image 
00162  * @param size   JPEG image size
00163  */
00164 GLOBAL(void)
00165 fv_jpeg_memory_source_setup(j_decompress_ptr cinfo, unsigned char *ptr, size_t size)
00166 {
00167     struct jpeg_source_mgr *src;
00168     src = cinfo->src = (struct jpeg_source_mgr *)
00169       (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo,
00170                                   JPOOL_PERMANENT,
00171                                   sizeof(*src));
00172     src->init_source       = init_source;
00173     src->fill_input_buffer = fill_input_buffer;
00174     src->skip_input_data   = skip_input_data;
00175     src->resync_to_restart = jpeg_resync_to_restart;
00176     src->term_source       = term_source;
00177     src->next_input_byte   = ptr;
00178     src->bytes_in_buffer   = size;
00179 }
00180 
00181 /// @endcond
00182 
00183 /** @class JpegImageCompressor <fvutils/compression/jpeg_compressor.h>
00184  * Jpeg image compressor.
00185  */
00186
00187 
00188 /** Constructor.
00189  * @param quality JPEG quality in percent
00190  * @param jcs Jpeg colorspace
00191  */
00192 JpegImageCompressor::JpegImageCompressor(unsigned int quality, JpegColorspace jcs)
00193 {
00194   this->quality = quality;
00195   jpeg_cs = jcs;
00196 }
00197 
00198 /** Destructor. */
00199 JpegImageCompressor::~JpegImageCompressor()
00200 {
00201 }
00202
00203
00204 void
00205 JpegImageCompressor::compress()
00206 {
00207   struct jpeg_compress_struct cinfo;
00208   struct jpeg_error_mgr jerr;
00209   unsigned int row_stride;
00210   unsigned char *row_buffer;
00211
00212   // mem destination specific
00213   fv_jpeg_memory_destination_mgr_t *dest;
00214
00215   // file destination specific
00216   FILE *outfile = NULL;
00217
00218   /* zero out the compression info structure and
00219      allocate a new compressor handle */
00220   memset (&cinfo, 0, sizeof(cinfo));
00221   cinfo.err = jpeg_std_error(&jerr);
00222   jpeg_create_compress(&cinfo);
00223
00224   /* Setup JPEG datastructures */
00225   cinfo.image_width = width;      /* image width and height, in pixels */
00226   cinfo.image_height = height;
00227   cinfo.input_components = 3;     /* # of color components per pixel=3 RGB */
00228   if ( jpeg_cs == JPEG_CS_RGB ) {
00229     cinfo.in_color_space = JCS_RGB;
00230   } else {
00231     cinfo.in_color_space = JCS_YCbCr;
00232   }
00233
00234   row_stride = cinfo.image_width * cinfo.input_components;
00235
00236   if ( compdest == COMP_DEST_MEM ) {
00237     // mem
00238     fv_jpeg_memory_destination_setup(&cinfo, (JOCTET *)jpeg_buffer, jpeg_buffer_size);
00239   } else {
00240     outfile = fopen(filename, "wb");
00241     if (outfile == NULL) {
00242       throw fawkes::Exception("JpegImageCompressor: cannot open %s\n", filename);
00243     }
00244     jpeg_stdio_dest( &cinfo, outfile );
00245   }
00246   /* Setup compression */
00247   jpeg_set_defaults(&cinfo);
00248   jpeg_set_quality (&cinfo, quality, true /* limit to baseline-JPEG values */);
00249   jpeg_start_compress(&cinfo, true);
00250
00251   /* compress each scanline one-at-a-time */
00252   row_buffer = (unsigned char *)malloc( row_stride );
00253
00254
00255   if ( jpeg_cs == JPEG_CS_RGB ) {
00256     while (cinfo.next_scanline < cinfo.image_height) {
00257       convert_line_yuv422planar_to_rgb( buffer, row_buffer,
00258                                         cinfo.image_width, cinfo.image_height,
00259                                         cinfo.next_scanline, 0 );
00260       jpeg_write_scanlines(&cinfo, &row_buffer, 1);
00261     }
00262   } else {
00263     while (cinfo.next_scanline < cinfo.image_height) {
00264       convert_line_yuv422planar_to_yuv444packed( buffer, row_buffer,
00265                                                  cinfo.image_width, cinfo.image_height,
00266                                                  cinfo.next_scanline, 0 );
00267       jpeg_write_scanlines(&cinfo, &row_buffer, 1);
00268     }
00269   }
00270
00271   free(row_buffer);
00272   jpeg_finish_compress(&cinfo);
00273
00274   if ( compdest == COMP_DEST_MEM ) {
00275     /* Now extract the size of the compressed buffer */
00276     dest=(fv_jpeg_memory_destination_mgr_t *)cinfo.dest;
00277     jpeg_bytes = dest->datacount; /* the actual compressed datasize */
00278   } else {
00279     fclose( outfile );
00280   }
00281
00282   /* destroy the compressor handle */
00283   jpeg_destroy_compress(&cinfo);
00284
00285 }
00286
00287
00288 void
00289 JpegImageCompressor::set_image_dimensions(unsigned int width, unsigned int height)
00290 {
00291   this->width = width;
00292   this->height = height;
00293 }
00294
00295
00296 void
00297 JpegImageCompressor::set_image_buffer(colorspace_t cspace, unsigned char *buffer)
00298 {
00299   if ( cspace == YUV422_PLANAR ) {
00300     this->buffer = buffer;
00301   }
00302 }
00303
00304
00305 void
00306 JpegImageCompressor::set_compression_destination(ImageCompressor::CompressionDestination cd)
00307 {
00308   compdest = cd;
00309 }
00310
00311
00312 bool
00313 JpegImageCompressor::supports_compression_destination(ImageCompressor::CompressionDestination cd)
00314 {
00315   return true;
00316 }
00317
00318
00319 void
00320 JpegImageCompressor::set_destination_buffer(unsigned char *buf, unsigned int buf_size)
00321 {
00322   jpeg_buffer = buf;
00323   jpeg_buffer_size = buf_size;
00324 }
00325
00326
00327 size_t
00328 JpegImageCompressor::compressed_size()
00329 {
00330   return jpeg_bytes;
00331 }
00332
00333 size_t
00334 JpegImageCompressor::recommended_compressed_buffer_size()
00335 {
00336   return width * height / 5;
00337 }
00338
00339
00340 void
00341 JpegImageCompressor::set_filename(const char *filename)
00342 {
00343   this->filename = filename;
00344 }