Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
jpeg_compressor.cpp
1 
2 /***************************************************************************
3  * jpeg_compressor.cpp - JPEG image compressor
4  *
5  * Created: Sat Aug 12 13:42:39 2006 (in LFI of Central Medical Library
6  * of Germany, Cologne)
7  * Copyright 2005-2011 Tim Niemueller [www.niemueller.de]
8  *
9  ****************************************************************************/
10 
11 /* This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version. A runtime exception applies to
15  * this software (see LICENSE.GPL_WRE file mentioned below for details).
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Library General Public License for more details.
21  *
22  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
23  */
24 
25 
26 #include <fvutils/compression/jpeg_compressor.h>
27 #include <fvutils/color/yuvrgb.h>
28 #include <fvutils/color/rgbyuv.h>
29 
30 #include <core/exception.h>
31 
32 #include <cstdio>
33 #include <cstring>
34 #include <cstdlib>
35 #include <setjmp.h>
36 extern "C" {
37 #include <jpeglib.h>
38 #include <jerror.h>
39 }
40 
41 namespace firevision {
42 #if 0 /* just to make Emacs auto-indent happy */
43 }
44 #endif
45 
46 ///@cond INTERNALS
47 
48 /** JPEG error manager type. */
49 typedef struct {
50  struct jpeg_error_mgr pub; /**< manager */
51  jmp_buf setjmp_buffer; /**< jmp buffer */
52 } fv_jpeg_error_mgr_t;
53 
54 
55 /** Defines a new destination manager to store images in memory
56  * derived by jdatadst.c
57  */
58 typedef struct {
59  struct jpeg_destination_mgr pub; /**< public fields */
60  JOCTET *buffer; /**< start of buffer */
61  int bufsize; /**< buffer size */
62  int datacount; /**< final data size */
63 } fv_jpeg_memory_destination_mgr_t;
64 
65 
66 /** Initialize destination
67  * called by jpeg_start_compress before any data is actually written.
68  * @param cinfo compression info
69  */
70 METHODDEF(void)
71 fv_jpeg_init_destination (j_compress_ptr cinfo)
72 {
73  fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
74  dest->pub.next_output_byte = dest->buffer;
75  dest->pub.free_in_buffer = dest->bufsize;
76  dest->datacount=0;
77 }
78 
79 /** Empty the output buffer
80  * called whenever buffer fills up.
81  * @param cinfo compression info
82  */
83 METHODDEF(boolean)
84 fv_jpeg_empty_output_buffer (j_compress_ptr cinfo)
85 {
86  fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
87  dest->pub.next_output_byte = dest->buffer;
88  dest->pub.free_in_buffer = dest->bufsize;
89 
90  return TRUE;
91 }
92 
93 /** Terminate destination
94  * called by jpeg_finish_compress after all data has been written.
95  * Usually needs to flush buffer.
96  * @param cinfo compression info
97  */
98 METHODDEF(void)
99 fv_jpeg_term_destination (j_compress_ptr cinfo)
100 {
101  /* expose the finale compressed image size */
102 
103  fv_jpeg_memory_destination_mgr_t *dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
104  dest->datacount = dest->bufsize - dest->pub.free_in_buffer;
105 }
106 
107 /** Setup memory destination.
108  * @param cinfo compression info
109  * @param buffer buffer
110  * @param bufsize buffer size
111  */
112 GLOBAL(void)
113 fv_jpeg_memory_destination_setup(j_compress_ptr cinfo, JOCTET *buffer,int bufsize)
114 {
115  fv_jpeg_memory_destination_mgr_t *dest;
116  if ( cinfo->dest == NULL ) {
117  cinfo->dest = (struct jpeg_destination_mgr *)
118  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
119  sizeof(fv_jpeg_memory_destination_mgr_t));
120 
121  }
122 
123  dest = (fv_jpeg_memory_destination_mgr_t *) cinfo->dest;
124  dest->bufsize = bufsize;
125  dest->buffer = buffer;
126  dest->pub.init_destination = fv_jpeg_init_destination;
127  dest->pub.empty_output_buffer = fv_jpeg_empty_output_buffer;
128  dest->pub.term_destination = fv_jpeg_term_destination;
129 }
130 
131 METHODDEF(void)
132 init_source(j_decompress_ptr cinfo)
133 {
134  /* nothing to do */
135 }
136 
137 METHODDEF(boolean)
138 fill_input_buffer(j_decompress_ptr cinfo)
139 {
140  /* can't fill */
141  return FALSE;
142 }
143 
144 METHODDEF(void)
145 skip_input_data(j_decompress_ptr cinfo, long num_bytes)
146 {
147  if ((size_t)num_bytes > cinfo->src->bytes_in_buffer) {
148  cinfo->src->next_input_byte = NULL;
149  cinfo->src->bytes_in_buffer = 0;
150  } else {
151  cinfo->src->next_input_byte += (size_t) num_bytes;
152  cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
153  }
154 }
155 
156 METHODDEF(void)
157 term_source(j_decompress_ptr cinfo)
158 {
159  /* nothing to do */
160 }
161 
162 METHODDEF(void)
163 fv_jpeg_error_exit(j_common_ptr cinfo)
164 {
165  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
166  fv_jpeg_error_mgr_t *myerr = (fv_jpeg_error_mgr_t *) cinfo->err;
167 
168  /* Return control to the setjmp point */
169  longjmp(myerr->setjmp_buffer, 1);
170 }
171 
172 
173 /**
174  * set momory-jpeg image to JPEG lib Info struct
175  * @param cinfo JPEG lib decompress infomation structure
176  * @param ptr JPEG image
177  * @param size JPEG image size
178  */
179 GLOBAL(void)
180 fv_jpeg_memory_source_setup(j_decompress_ptr cinfo, unsigned char *ptr, size_t size)
181 {
182  struct jpeg_source_mgr *src;
183  src = cinfo->src = (struct jpeg_source_mgr *)
184  (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo,
185  JPOOL_PERMANENT,
186  sizeof(*src));
187  src->init_source = init_source;
188  src->fill_input_buffer = fill_input_buffer;
189  src->skip_input_data = skip_input_data;
190  src->resync_to_restart = jpeg_resync_to_restart;
191  src->term_source = term_source;
192  src->next_input_byte = ptr;
193  src->bytes_in_buffer = size;
194 }
195 
196 /// @endcond
197 
198 /** @class JpegImageCompressor <fvutils/compression/jpeg_compressor.h>
199  * Jpeg image compressor.
200  */
201 
202 
203 /** Constructor.
204  * @param quality JPEG quality in percent
205  * @param jcs Jpeg colorspace
206  */
207 JpegImageCompressor::JpegImageCompressor(unsigned int quality, JpegColorspace jcs)
208 {
209  this->quality = quality;
210  jpeg_cs = jcs;
211 }
212 
213 /** Destructor. */
214 JpegImageCompressor::~JpegImageCompressor()
215 {
216 }
217 
218 
219 void
220 JpegImageCompressor::compress()
221 {
222  struct jpeg_compress_struct cinfo;
223  fv_jpeg_error_mgr_t jerr;
224  unsigned int row_stride;
225  unsigned char *row_buffer;
226 
227  // mem destination specific
228  fv_jpeg_memory_destination_mgr_t *dest;
229 
230  // file destination specific
231  FILE *outfile = NULL;
232 
233  /* zero out the compression info structure and
234  allocate a new compressor handle */
235  memset (&cinfo, 0, sizeof(cinfo));
236  cinfo.err = jpeg_std_error(&jerr.pub);
237  jerr.pub.error_exit = fv_jpeg_error_exit;
238 
239  /* Establish the setjmp return context for my_error_exit to use. */
240  if (setjmp(jerr.setjmp_buffer)) {
241  char buffer[JMSG_LENGTH_MAX];
242  (*cinfo.err->format_message) ((jpeg_common_struct *)&cinfo, buffer);
243 
244  /* If we get here, the JPEG code has signaled an error.
245  * We need to clean up the JPEG object, close the input file, and return.
246  */
247  jpeg_destroy_compress(&cinfo);
248  throw fawkes::Exception("Compression failed: %s", buffer);
249  }
250 
251  jpeg_create_compress(&cinfo);
252 
253  /* Setup JPEG datastructures */
254  cinfo.image_width = width; /* image width and height, in pixels */
255  cinfo.image_height = height;
256  cinfo.input_components = 3; /* # of color components per pixel=3 RGB */
257  if ( jpeg_cs == JPEG_CS_RGB ) {
258  cinfo.in_color_space = JCS_RGB;
259  } else {
260  cinfo.in_color_space = JCS_YCbCr;
261  }
262 
263  row_stride = cinfo.image_width * cinfo.input_components;
264 
265  if ( compdest == COMP_DEST_MEM ) {
266  // mem
267  fv_jpeg_memory_destination_setup(&cinfo, (JOCTET *)jpeg_buffer, jpeg_buffer_size);
268  } else {
269  outfile = fopen(filename, "wb");
270  if (outfile == NULL) {
271  throw fawkes::Exception("JpegImageCompressor: cannot open %s\n", filename);
272  }
273  jpeg_stdio_dest( &cinfo, outfile );
274  }
275  /* Setup compression */
276  jpeg_set_defaults(&cinfo);
277  jpeg_set_quality (&cinfo, quality, true /* limit to baseline-JPEG values */);
278  jpeg_start_compress(&cinfo, true);
279 
280  /* compress each scanline one-at-a-time */
281  row_buffer = (unsigned char *)malloc( row_stride );
282 
283 
284  if ( jpeg_cs == JPEG_CS_RGB ) {
285  while (cinfo.next_scanline < cinfo.image_height) {
286  convert_line_yuv422planar_to_rgb( buffer, row_buffer,
287  cinfo.image_width, cinfo.image_height,
288  cinfo.next_scanline, 0 );
289  jpeg_write_scanlines(&cinfo, &row_buffer, 1);
290  }
291  } else {
292  while (cinfo.next_scanline < cinfo.image_height) {
293  convert_line_yuv422planar_to_yuv444packed( buffer, row_buffer,
294  cinfo.image_width, cinfo.image_height,
295  cinfo.next_scanline, 0 );
296  jpeg_write_scanlines(&cinfo, &row_buffer, 1);
297  }
298  }
299 
300  free(row_buffer);
301  jpeg_finish_compress(&cinfo);
302 
303  if ( compdest == COMP_DEST_MEM ) {
304  /* Now extract the size of the compressed buffer */
305  dest=(fv_jpeg_memory_destination_mgr_t *)cinfo.dest;
306  jpeg_bytes = dest->datacount; /* the actual compressed datasize */
307  } else {
308  fclose( outfile );
309  }
310 
311  /* destroy the compressor handle */
312  jpeg_destroy_compress(&cinfo);
313 
314 }
315 
316 
317 void
318 JpegImageCompressor::set_image_dimensions(unsigned int width, unsigned int height)
319 {
320  this->width = width;
321  this->height = height;
322 }
323 
324 
325 void
326 JpegImageCompressor::set_image_buffer(colorspace_t cspace, unsigned char *buffer)
327 {
328  if ( cspace == YUV422_PLANAR ) {
329  this->buffer = buffer;
330  }
331 }
332 
333 
334 void
335 JpegImageCompressor::set_compression_destination(ImageCompressor::CompressionDestination cd)
336 {
337  compdest = cd;
338 }
339 
340 
341 bool
342 JpegImageCompressor::supports_compression_destination(ImageCompressor::CompressionDestination cd)
343 {
344  return true;
345 }
346 
347 
348 void
349 JpegImageCompressor::set_destination_buffer(unsigned char *buf, unsigned int buf_size)
350 {
351  jpeg_buffer = buf;
352  jpeg_buffer_size = buf_size;
353 }
354 
355 
356 size_t
357 JpegImageCompressor::compressed_size()
358 {
359  return jpeg_bytes;
360 }
361 
362 size_t
363 JpegImageCompressor::recommended_compressed_buffer_size()
364 {
365  return width * height / 4;
366 }
367 
368 
369 void
370 JpegImageCompressor::set_filename(const char *filename)
371 {
372  this->filename = filename;
373 }
374 
375 } // end namespace firevision
CompressionDestination
Where to put the compressed image.
Base class for exceptions in Fawkes.
Definition: exception.h:36