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