Fawkes API  Fawkes Development Version
jpeg_compressor_libjpeg.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 #include <fvutils/compression/jpeg_compressor.h>
26 #include <fvutils/compression/jpeg_compressor_libjpeg.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 JpegImageCompressorLibJpeg <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  */
209 {
210  this->quality = quality;
211  jpeg_cs = jcs;
212 }
213 
214 /** Destructor. */
216 {
217 }
218 
219 
220 bool
222 {
223  return true;
224 }
225 
226 void
228 {
229  vflip = enabled;
230 }
231 
232 void
234 {
235  struct jpeg_compress_struct cinfo;
236  fv_jpeg_error_mgr_t jerr;
237  unsigned int row_stride;
238  unsigned char *row_buffer;
239 
240  // mem destination specific
241  fv_jpeg_memory_destination_mgr_t *dest;
242 
243  // file destination specific
244  FILE *outfile = NULL;
245 
246  /* zero out the compression info structure and
247  allocate a new compressor handle */
248  memset (&cinfo, 0, sizeof(cinfo));
249  cinfo.err = jpeg_std_error(&jerr.pub);
250  jerr.pub.error_exit = fv_jpeg_error_exit;
251 
252  /* Establish the setjmp return context for my_error_exit to use. */
253  if (setjmp(jerr.setjmp_buffer)) {
254  char buffer[JMSG_LENGTH_MAX];
255  (*cinfo.err->format_message) ((jpeg_common_struct *)&cinfo, buffer);
256 
257  /* If we get here, the JPEG code has signaled an error.
258  * We need to clean up the JPEG object, close the input file, and return.
259  */
260  jpeg_destroy_compress(&cinfo);
261  throw fawkes::Exception("Compression failed: %s", buffer);
262  }
263 
264  jpeg_create_compress(&cinfo);
265 
266  /* Setup JPEG datastructures */
267  cinfo.image_width = width; /* image width and height, in pixels */
268  cinfo.image_height = height;
269  cinfo.input_components = 3; /* # of color components per pixel=3 RGB */
270  if ( jpeg_cs == JpegImageCompressor::JPEG_CS_RGB ) {
271  cinfo.in_color_space = JCS_RGB;
272  } else {
273  cinfo.in_color_space = JCS_YCbCr;
274  }
275 
276  row_stride = cinfo.image_width * cinfo.input_components;
277 
278  if ( compdest == COMP_DEST_MEM ) {
279  // mem
280  fv_jpeg_memory_destination_setup(&cinfo, (JOCTET *)jpeg_buffer, jpeg_buffer_size);
281  } else {
282  outfile = fopen(filename, "wb");
283  if (outfile == NULL) {
284  throw fawkes::Exception("JpegImageCompressorLibJpeg: cannot open %s\n", filename);
285  }
286  jpeg_stdio_dest( &cinfo, outfile );
287  }
288  /* Setup compression */
289  jpeg_set_defaults(&cinfo);
290  jpeg_set_quality (&cinfo, quality, true /* limit to baseline-JPEG values */);
291  jpeg_start_compress(&cinfo, true);
292 
293  /* compress each scanline one-at-a-time */
294  row_buffer = (unsigned char *)malloc( row_stride );
295 
296 
297  if ( jpeg_cs == JpegImageCompressor::JPEG_CS_RGB ) {
298  if (vflip) {
299  while (cinfo.next_scanline < cinfo.image_height) {
300  convert_line_yuv422planar_to_rgb(
301  buffer, row_buffer,
302  cinfo.image_width, cinfo.image_height,
303  cinfo.image_height - cinfo.next_scanline - 1, 0);
304  jpeg_write_scanlines(&cinfo, &row_buffer, 1);
305  }
306  } else {
307  while (cinfo.next_scanline < cinfo.image_height) {
308  convert_line_yuv422planar_to_rgb(
309  buffer, row_buffer,
310  cinfo.image_width, cinfo.image_height,
311  cinfo.next_scanline, 0 );
312  jpeg_write_scanlines(&cinfo, &row_buffer, 1);
313  }
314  }
315  } else {
316  if (vflip) {
317  while (cinfo.next_scanline < cinfo.image_height) {
318  convert_line_yuv422planar_to_yuv444packed(
319  buffer, row_buffer,
320  cinfo.image_width, cinfo.image_height,
321  cinfo.image_height - cinfo.next_scanline - 1, 0 );
322  jpeg_write_scanlines(&cinfo, &row_buffer, 1);
323  }
324  } else {
325  while (cinfo.next_scanline < cinfo.image_height) {
326  convert_line_yuv422planar_to_yuv444packed(
327  buffer, row_buffer,
328  cinfo.image_width, cinfo.image_height,
329  cinfo.next_scanline, 0 );
330  jpeg_write_scanlines(&cinfo, &row_buffer, 1);
331  }
332  }
333  }
334 
335  free(row_buffer);
336  jpeg_finish_compress(&cinfo);
337 
338  if ( compdest == COMP_DEST_MEM ) {
339  /* Now extract the size of the compressed buffer */
340  dest=(fv_jpeg_memory_destination_mgr_t *)cinfo.dest;
341  jpeg_bytes = dest->datacount; /* the actual compressed datasize */
342  } else {
343  fclose( outfile );
344  }
345 
346  /* destroy the compressor handle */
347  jpeg_destroy_compress(&cinfo);
348 
349 }
350 
351 
352 void
353 JpegImageCompressorLibJpeg::set_image_dimensions(unsigned int width, unsigned int height)
354 {
355  this->width = width;
356  this->height = height;
357 }
358 
359 
360 void
361 JpegImageCompressorLibJpeg::set_image_buffer(colorspace_t cspace, unsigned char *buffer)
362 {
363  if ( cspace == YUV422_PLANAR ) {
364  this->buffer = buffer;
365  }
366 }
367 
368 
369 void
371 {
372  compdest = cd;
373 }
374 
375 
376 bool
378 {
379  return true;
380 }
381 
382 
383 void
384 JpegImageCompressorLibJpeg::set_destination_buffer(unsigned char *buf, unsigned int buf_size)
385 {
386  jpeg_buffer = buf;
387  jpeg_buffer_size = buf_size;
388 }
389 
390 
391 size_t
393 {
394  return jpeg_bytes;
395 }
396 
397 size_t
399 {
400  return width * height / 4;
401 }
402 
403 
404 void
406 {
407  this->filename = filename;
408 }
409 
410 } // end namespace firevision
virtual bool supports_compression_destination(ImageCompressor::CompressionDestination cd)
Check if compressor supports desired compression destination.
virtual void set_image_buffer(colorspace_t cspace, unsigned char *buffer)
Set image buffer to compress.
virtual bool supports_vflip()
Check if image compressor can do vflip during compress.
virtual void set_compression_destination(ImageCompressor::CompressionDestination cd)
Set compression destination.
virtual void set_image_dimensions(unsigned int width, unsigned int height)
Set dimensions of image to compress.
CompressionDestination
Where to put the compressed image.
JpegImageCompressorLibJpeg(unsigned int quality=80, JpegImageCompressor::JpegColorspace jcs=JpegImageCompressor::JPEG_CS_RGB)
Constructor.
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual size_t compressed_size()
Get compressed size.
virtual void set_vflip(bool enable)
Enable or disable vflipping.
virtual void set_destination_buffer(unsigned char *buf, unsigned int buf_size)
Set destination buffer (if compressing to memory).
virtual void set_filename(const char *filename)
Set file name.
virtual size_t recommended_compressed_buffer_size()
Get the recommended size for the compressed buffer.