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>
35extern "C" {
36#include <jerror.h>
37#include <jpeglib.h>
38}
39
40namespace firevision {
41
42///@cond INTERNALS
43
44/** JPEG error manager type. */
45typedef 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 */
54typedef 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 */
66METHODDEF(void)
67fv_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 */
79METHODDEF(boolean)
80fv_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 */
94METHODDEF(void)
95fv_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 */
108GLOBAL(void)
109fv_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
125METHODDEF(void)
126init_source(j_decompress_ptr cinfo)
127{
128 /* nothing to do */
129}
130
131METHODDEF(boolean)
132fill_input_buffer(j_decompress_ptr cinfo)
133{
134 /* can't fill */
135 return FALSE;
136}
137
138METHODDEF(void)
139skip_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
150METHODDEF(void)
151term_source(j_decompress_ptr cinfo)
152{
153 /* nothing to do */
154}
155
156METHODDEF(void)
157fv_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 */
172GLOBAL(void)
173fv_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
210bool
212{
213 return true;
214}
215
216void
218{
219 vflip = enabled;
220}
221
222void
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
339void
340JpegImageCompressorLibJpeg::set_image_dimensions(unsigned int width, unsigned int height)
341{
342 this->width = width;
343 this->height = height;
344}
345
346void
347JpegImageCompressorLibJpeg::set_image_buffer(colorspace_t cspace, unsigned char *buffer)
348{
349 if (cspace == YUV422_PLANAR) {
350 this->buffer = buffer;
351 }
352}
353
354void
356{
357 compdest = cd;
358}
359
360bool
363{
364 return true;
365}
366
367void
368JpegImageCompressorLibJpeg::set_destination_buffer(unsigned char *buf, unsigned int buf_size)
369{
370 jpeg_buffer = buf;
371 jpeg_buffer_size = buf_size;
372}
373
374size_t
376{
377 return jpeg_bytes;
378}
379
380size_t
382{
383 return width * height / 4;
384}
385
386void
388{
389 this->filename = filename;
390}
391
392} // end namespace firevision
Base class for exceptions in Fawkes.
Definition: exception.h:36
CompressionDestination
Where to put the compressed image.
@ COMP_DEST_MEM
write compressed image to buffer in memory
virtual void set_filename(const char *filename)
Set file name.
virtual void set_vflip(bool enable)
Enable or disable vflipping.
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 size_t recommended_compressed_buffer_size()
Get the recommended size for the compressed buffer.
virtual bool supports_vflip()
Check if image compressor can do vflip during compress.
JpegImageCompressorLibJpeg(unsigned int quality=80, JpegImageCompressor::JpegColorspace jcs=JpegImageCompressor::JPEG_CS_RGB)
Constructor.
virtual void set_compression_destination(ImageCompressor::CompressionDestination cd)
Set compression destination.
virtual void set_destination_buffer(unsigned char *buf, unsigned int buf_size)
Set destination buffer (if compressing to memory).
virtual void set_image_dimensions(unsigned int width, unsigned int height)
Set dimensions of image to compress.
virtual size_t compressed_size()
Get compressed size.