Devel::CheckLib: handle the gcc-4 symlink on Cygwin
[imager.git] / JPEG / imjpeg.c
CommitLineData
f873cb01
TC
1/*
2=head1 NAME
3
4jpeg.c - implement saving and loading JPEG images
5
6=head1 SYNOPSIS
7
8 io_glue *ig;
9 if (!i_writejpeg_wiol(im, ig, quality)) {
10 .. error ..
11 }
12 im = i_readjpeg_wiol(ig, length, iptc_text, itlength);
13
14=head1 DESCRIPTION
15
16Reads and writes JPEG images
17
18=over
19
20=cut
21*/
22
797a9f9c
TC
23#include "imjpeg.h"
24#include "imext.h"
02d1d628
AMH
25#include <stdio.h>
26#include <sys/stat.h>
0a651031 27#ifndef _MSC_VER
02d1d628 28#include <unistd.h>
0a651031 29#endif
02d1d628 30#include <setjmp.h>
bb4a69a1 31#include <string.h>
02d1d628 32
02d1d628 33#include "jpeglib.h"
f873cb01
TC
34#include "jerror.h"
35#include <errno.h>
797a9f9c 36#include <stdlib.h>
8d14daab 37#include "imexif.h"
02d1d628 38
dd55acc8 39#define JPEG_APP13 0xED /* APP13 marker code */
f7450478 40#define JPEG_APP1 (JPEG_APP0 + 1)
412e7a35 41#define JPGS 16384
02d1d628 42
8d14daab
TC
43#define JPEG_DIM_MAX JPEG_MAX_DIMENSION
44
d20ad5ba
TC
45#define _STRINGIFY(x) #x
46#define STRINGIFY(x) _STRINGIFY(x)
47
b33c08f8 48static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
02d1d628 49
dd55acc8 50/* Source and Destination managers */
02d1d628 51
dd55acc8
AMH
52typedef struct {
53 struct jpeg_source_mgr pub; /* public fields */
54 io_glue *data;
55 JOCTET *buffer; /* start of buffer */
56 int length; /* Do I need this? */
57 boolean start_of_file; /* have we gotten any data yet? */
58} wiol_source_mgr;
02d1d628 59
dd55acc8
AMH
60typedef struct {
61 struct jpeg_destination_mgr pub; /* public fields */
62 io_glue *data;
63 JOCTET *buffer; /* start of buffer */
64 boolean start_of_file; /* have we gotten any data yet? */
65} wiol_destination_mgr;
02d1d628 66
dd55acc8
AMH
67typedef wiol_source_mgr *wiol_src_ptr;
68typedef wiol_destination_mgr *wiol_dest_ptr;
02d1d628 69
dd55acc8
AMH
70/*
71 * Methods for io manager objects
72 *
73 * Methods for source managers:
74 *
75 * init_source (j_decompress_ptr cinfo);
76 * skip_input_data (j_decompress_ptr cinfo, long num_bytes);
77 * fill_input_buffer (j_decompress_ptr cinfo);
78 * term_source (j_decompress_ptr cinfo);
79 */
80
02d1d628
AMH
81
82
83static void
dd55acc8
AMH
84wiol_init_source (j_decompress_ptr cinfo) {
85 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
02d1d628 86
dd55acc8
AMH
87 /* We reset the empty-input-file flag for each image, but we don't clear
88 * the input buffer. This is correct behavior for reading a series of
89 * images from one source.
02d1d628
AMH
90 */
91 src->start_of_file = TRUE;
92}
93
dd55acc8
AMH
94
95
02d1d628 96static boolean
dd55acc8
AMH
97wiol_fill_input_buffer(j_decompress_ptr cinfo) {
98 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
99 ssize_t nbytes; /* We assume that reads are "small" */
100
8d14daab 101 mm_log((1,"wiol_fill_input_buffer(cinfo %p)\n", cinfo));
02d1d628 102
6d5c85a2 103 nbytes = i_io_read(src->data, src->buffer, JPGS);
dd55acc8
AMH
104
105 if (nbytes <= 0) { /* Insert a fake EOI marker */
106 src->pub.next_input_byte = fake_eoi;
107 src->pub.bytes_in_buffer = 2;
02d1d628 108 } else {
dd55acc8
AMH
109 src->pub.next_input_byte = src->buffer;
110 src->pub.bytes_in_buffer = nbytes;
02d1d628 111 }
02d1d628
AMH
112 src->start_of_file = FALSE;
113 return TRUE;
114}
115
dd55acc8 116
02d1d628 117static void
dd55acc8
AMH
118wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
119 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
02d1d628
AMH
120
121 /* Just a dumb implementation for now. Could use fseek() except
122 * it doesn't work on pipes. Not clear that being smart is worth
123 * any trouble anyway --- large skips are infrequent.
124 */
125
126 if (num_bytes > 0) {
127 while (num_bytes > (long) src->pub.bytes_in_buffer) {
128 num_bytes -= (long) src->pub.bytes_in_buffer;
dd55acc8 129 (void) wiol_fill_input_buffer(cinfo);
02d1d628
AMH
130 /* note we assume that fill_input_buffer will never return FALSE,
131 * so suspension need not be handled.
132 */
133 }
134 src->pub.next_input_byte += (size_t) num_bytes;
135 src->pub.bytes_in_buffer -= (size_t) num_bytes;
136 }
137}
138
139static void
dd55acc8
AMH
140wiol_term_source (j_decompress_ptr cinfo) {
141 /* no work necessary here */
142 wiol_src_ptr src;
143 if (cinfo->src != NULL) {
144 src = (wiol_src_ptr) cinfo->src;
145 myfree(src->buffer);
146 }
147}
148
149
150/* Source manager Constructor */
02d1d628
AMH
151
152static void
dd55acc8
AMH
153jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
154 wiol_src_ptr src;
02d1d628
AMH
155
156 if (cinfo->src == NULL) { /* first time for this JPEG object? */
dd55acc8
AMH
157 cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
158 ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(wiol_source_mgr));
159 src = (wiol_src_ptr) cinfo->src;
02d1d628 160 }
dd55acc8
AMH
161
162 /* put the request method call in here later */
02d1d628 163
dd55acc8
AMH
164 src = (wiol_src_ptr) cinfo->src;
165 src->data = ig;
166 src->buffer = mymalloc( JPGS );
02d1d628 167 src->length = length;
dd55acc8
AMH
168
169 src->pub.init_source = wiol_init_source;
170 src->pub.fill_input_buffer = wiol_fill_input_buffer;
171 src->pub.skip_input_data = wiol_skip_input_data;
02d1d628 172 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
dd55acc8
AMH
173 src->pub.term_source = wiol_term_source;
174 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
175 src->pub.next_input_byte = NULL; /* until buffer loaded */
02d1d628
AMH
176}
177
178
179
180
dd55acc8
AMH
181/*
182 * Methods for destination managers:
183 *
184 * init_destination (j_compress_ptr cinfo);
185 * empty_output_buffer (j_compress_ptr cinfo);
186 * term_destination (j_compress_ptr cinfo);
187 *
188 */
02d1d628 189
dd55acc8
AMH
190static void
191wiol_init_destination (j_compress_ptr cinfo) {
192 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
02d1d628 193
dd55acc8
AMH
194 /* We reset the empty-input-file flag for each image, but we don't clear
195 * the input buffer. This is correct behavior for reading a series of
196 * images from one source.
02d1d628 197 */
dd55acc8
AMH
198 dest->start_of_file = TRUE;
199}
02d1d628 200
dd55acc8
AMH
201static boolean
202wiol_empty_output_buffer(j_compress_ptr cinfo) {
203 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
dd55acc8 204 ssize_t rc;
cf692b64
TC
205 /*
206 Previously this code was checking free_in_buffer to see how much
207 needed to be written. This does not follow the documentation:
208
209 "In typical applications, it should write out the
210 *entire* buffer (use the saved start address and buffer length;
211 ignore the current state of next_output_byte and free_in_buffer)."
212
213 ssize_t nbytes = JPGS - dest->pub.free_in_buffer;
214 */
02d1d628 215
8d14daab 216 mm_log((1,"wiol_empty_output_buffer(cinfo %p)\n", cinfo));
6d5c85a2 217 rc = i_io_write(dest->data, dest->buffer, JPGS);
a73aeb5f 218
cf692b64 219 if (rc != JPGS) { /* XXX: Should raise some jpeg error */
a73aeb5f 220 myfree(dest->buffer);
5e84d110 221 mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, (int)rc));
a73aeb5f 222 ERREXIT(cinfo, JERR_FILE_WRITE);
02d1d628 223 }
dd55acc8
AMH
224 dest->pub.free_in_buffer = JPGS;
225 dest->pub.next_output_byte = dest->buffer;
226 return TRUE;
227}
02d1d628 228
dd55acc8
AMH
229static void
230wiol_term_destination (j_compress_ptr cinfo) {
231 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
f873cb01 232 size_t nbytes = JPGS - dest->pub.free_in_buffer;
cf692b64
TC
233 /* yes, this needs to flush the buffer */
234 /* needs error handling */
a73aeb5f 235
6d5c85a2 236 if (i_io_write(dest->data, dest->buffer, nbytes) != nbytes) {
a73aeb5f 237 myfree(dest->buffer);
f873cb01
TC
238 ERREXIT(cinfo, JERR_FILE_WRITE);
239 }
6d5c85a2 240
dd55acc8
AMH
241 if (dest != NULL) myfree(dest->buffer);
242}
02d1d628 243
02d1d628 244
dd55acc8 245/* Destination manager Constructor */
02d1d628 246
dd55acc8
AMH
247static void
248jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
249 wiol_dest_ptr dest;
02d1d628 250
dd55acc8
AMH
251 if (cinfo->dest == NULL) { /* first time for this JPEG object? */
252 cinfo->dest =
253 (struct jpeg_destination_mgr *)
254 (*cinfo->mem->alloc_small)
255 ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
02d1d628 256 }
dd55acc8
AMH
257
258 dest = (wiol_dest_ptr) cinfo->dest;
259 dest->data = ig;
260 dest->buffer = mymalloc( JPGS );
261
262 dest->pub.init_destination = wiol_init_destination;
263 dest->pub.empty_output_buffer = wiol_empty_output_buffer;
264 dest->pub.term_destination = wiol_term_destination;
265 dest->pub.free_in_buffer = JPGS;
266 dest->pub.next_output_byte = dest->buffer;
02d1d628
AMH
267}
268
02d1d628 269METHODDEF(void)
dd55acc8 270my_output_message (j_common_ptr cinfo) {
02d1d628
AMH
271 char buffer[JMSG_LENGTH_MAX];
272
273 /* Create the message */
274 (*cinfo->err->format_message) (cinfo, buffer);
275
f873cb01
TC
276 i_push_error(0, buffer);
277
02d1d628
AMH
278 /* Send it to stderr, adding a newline */
279 mm_log((1, "%s\n", buffer));
280}
281
02d1d628
AMH
282struct my_error_mgr {
283 struct jpeg_error_mgr pub; /* "public" fields */
284 jmp_buf setjmp_buffer; /* for return to caller */
285};
286
287typedef struct my_error_mgr * my_error_ptr;
288
289/* Here's the routine that will replace the standard error_exit method */
290
291METHODDEF(void)
292my_error_exit (j_common_ptr cinfo) {
293 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
294 my_error_ptr myerr = (my_error_ptr) cinfo->err;
295
296 /* Always display the message. */
297 /* We could postpone this until after returning, if we chose. */
298 (*cinfo->err->output_message) (cinfo);
f873cb01 299
02d1d628
AMH
300 /* Return control to the setjmp point */
301 longjmp(myerr->setjmp_buffer, 1);
302}
303
02ea5e47
TC
304static void
305transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
306 JSAMPROW inrow = *in;
307 while (width--) {
308 /* extract and convert to real CMYK */
309 /* horribly enough this is correct given cmyk values are inverted */
310 int c = *inrow++;
311 int m = *inrow++;
312 int y = *inrow++;
313 int k = *inrow++;
314 out->rgba.r = (c * k) / MAXJSAMPLE;
315 out->rgba.g = (m * k) / MAXJSAMPLE;
316 out->rgba.b = (y * k) / MAXJSAMPLE;
317 ++out;
318 }
319}
320
321static void
322transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
323 JSAMPROW inrow = *in;
324 while (width--) {
325 out->rgba.r = *inrow++;
326 out->rgba.g = *inrow++;
327 out->rgba.b = *inrow++;
328 ++out;
329 }
330}
331
332static void
333transfer_gray(i_color *out, JSAMPARRAY in, int width) {
334 JSAMPROW inrow = *in;
335 while (width--) {
336 out->gray.gray_color = *inrow++;
337 ++out;
338 }
339}
340
341typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
342
d20ad5ba
TC
343static const char version_string[] =
344#ifdef LIBJPEG_TURBO_VERSION
345 "libjpeg-turbo " STRINGIFY(LIBJPEG_TURBO_VERSION) " api " STRINGIFY(JPEG_LIB_VERSION)
346#else
347 "libjpeg " STRINGIFY(JPEG_LIB_VERSION)
348#endif
349 ;
350
351/*
352=item i_libjpeg_version()
353
354=cut
355*/
356
357const char *
358i_libjpeg_version(void) {
359 return version_string;
360}
361
f873cb01
TC
362/*
363=item i_readjpeg_wiol(data, length, iptc_itext, itlength)
02d1d628 364
f873cb01
TC
365=cut
366*/
02d1d628 367i_img*
dd55acc8 368i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
02576e8d 369 i_img * volatile im = NULL;
f7450478 370 int seen_exif = 0;
546ea21c 371 i_color * volatile line_buffer = NULL;
02d1d628 372 struct jpeg_decompress_struct cinfo;
02d1d628 373 struct my_error_mgr jerr;
02d1d628
AMH
374 JSAMPARRAY buffer; /* Output row buffer */
375 int row_stride; /* physical row width in output buffer */
f7450478 376 jpeg_saved_marker_ptr markerp;
02ea5e47
TC
377 transfer_function_t transfer_f;
378 int channels;
02576e8d 379 volatile int src_set = 0;
02d1d628 380
8d14daab 381 mm_log((1,"i_readjpeg_wiol(data %p, length %d,iptc_itext %p)\n", data, length, iptc_itext));
02d1d628 382
f873cb01
TC
383 i_clear_error();
384
bb4a69a1
TC
385 *iptc_itext = NULL;
386 *itlength = 0;
387
02d1d628 388 cinfo.err = jpeg_std_error(&jerr.pub);
dd55acc8 389 jerr.pub.error_exit = my_error_exit;
02d1d628 390 jerr.pub.output_message = my_output_message;
02d1d628 391
dd55acc8 392 /* Set error handler */
02d1d628 393 if (setjmp(jerr.setjmp_buffer)) {
02576e8d
TC
394 if (src_set)
395 wiol_term_source(&cinfo);
02d1d628 396 jpeg_destroy_decompress(&cinfo);
02ea5e47
TC
397 if (line_buffer)
398 myfree(line_buffer);
02576e8d
TC
399 if (im)
400 i_img_destroy(im);
02d1d628
AMH
401 return NULL;
402 }
403
404 jpeg_create_decompress(&cinfo);
bb4a69a1 405 jpeg_save_markers(&cinfo, JPEG_APP13, 0xFFFF);
f7450478
TC
406 jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
407 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
dd55acc8 408 jpeg_wiol_src(&cinfo, data, length);
02576e8d 409 src_set = 1;
dd55acc8 410
02d1d628
AMH
411 (void) jpeg_read_header(&cinfo, TRUE);
412 (void) jpeg_start_decompress(&cinfo);
02ea5e47
TC
413
414 channels = cinfo.output_components;
415 switch (cinfo.out_color_space) {
416 case JCS_GRAYSCALE:
a840253f
TC
417 if (cinfo.output_components != 1) {
418 mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components));
419 i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components);
420 wiol_term_source(&cinfo);
421 jpeg_destroy_decompress(&cinfo);
422 return NULL;
423 }
02ea5e47
TC
424 transfer_f = transfer_gray;
425 break;
426
427 case JCS_RGB:
428 transfer_f = transfer_rgb;
a840253f
TC
429 if (cinfo.output_components != 3) {
430 mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components));
431 i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components);
432 wiol_term_source(&cinfo);
433 jpeg_destroy_decompress(&cinfo);
434 return NULL;
435 }
02ea5e47
TC
436 break;
437
438 case JCS_CMYK:
439 if (cinfo.output_components == 4) {
440 /* we treat the CMYK values as inverted, because that's what that
441 buggy photoshop does, and everyone has to follow the gorilla.
442
443 Is there any app that still produces correct CMYK JPEGs?
444 */
445 transfer_f = transfer_cmyk_inverted;
446 channels = 3;
447 }
448 else {
449 mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components));
450 i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components);
451 wiol_term_source(&cinfo);
452 jpeg_destroy_decompress(&cinfo);
453 return NULL;
454 }
455 break;
456
457 default:
458 mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space));
459 i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space);
460 wiol_term_source(&cinfo);
461 jpeg_destroy_decompress(&cinfo);
462 return NULL;
463 }
464
77157728 465 if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
02ea5e47 466 channels, sizeof(i_sample_t))) {
77157728 467 mm_log((1, "i_readjpeg: image size exceeds limits\n"));
22845485 468 wiol_term_source(&cinfo);
77157728
TC
469 jpeg_destroy_decompress(&cinfo);
470 return NULL;
471 }
02ea5e47 472
797a9f9c 473 im = i_img_8_new(cinfo.output_width, cinfo.output_height, channels);
2c2c832a 474 if (!im) {
22845485 475 wiol_term_source(&cinfo);
2c2c832a
TC
476 jpeg_destroy_decompress(&cinfo);
477 return NULL;
478 }
02d1d628
AMH
479 row_stride = cinfo.output_width * cinfo.output_components;
480 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
02ea5e47 481 line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
02d1d628
AMH
482 while (cinfo.output_scanline < cinfo.output_height) {
483 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
02ea5e47
TC
484 transfer_f(line_buffer, buffer, cinfo.output_width);
485 i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
02d1d628 486 }
02ea5e47 487 myfree(line_buffer);
546ea21c 488 line_buffer = NULL;
f7450478
TC
489
490 /* check for APP1 marker and save */
491 markerp = cinfo.marker_list;
492 while (markerp != NULL) {
493 if (markerp->marker == JPEG_COM) {
797a9f9c
TC
494 i_tags_set(&im->tags, "jpeg_comment", (const char *)markerp->data,
495 markerp->data_length);
f7450478 496 }
f7450478
TC
497 else if (markerp->marker == JPEG_APP1 && !seen_exif) {
498 seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
499 }
bb4a69a1
TC
500 else if (markerp->marker == JPEG_APP13) {
501 *iptc_itext = mymalloc(markerp->data_length);
502 memcpy(*iptc_itext, markerp->data, markerp->data_length);
503 *itlength = markerp->data_length;
504 }
f7450478
TC
505
506 markerp = markerp->next;
507 }
508
797a9f9c
TC
509 i_tags_setn(&im->tags, "jpeg_out_color_space", cinfo.out_color_space);
510 i_tags_setn(&im->tags, "jpeg_color_space", cinfo.jpeg_color_space);
02ea5e47 511
6d54291b
TC
512 if (cinfo.saw_JFIF_marker) {
513 double xres = cinfo.X_density;
514 double yres = cinfo.Y_density;
515
797a9f9c 516 i_tags_setn(&im->tags, "jpeg_density_unit", cinfo.density_unit);
6d54291b
TC
517 switch (cinfo.density_unit) {
518 case 0: /* values are just the aspect ratio */
797a9f9c
TC
519 i_tags_setn(&im->tags, "i_aspect_only", 1);
520 i_tags_set(&im->tags, "jpeg_density_unit_name", "none", -1);
6d54291b
TC
521 break;
522
523 case 1: /* per inch */
797a9f9c 524 i_tags_set(&im->tags, "jpeg_density_unit_name", "inch", -1);
6d54291b
TC
525 break;
526
527 case 2: /* per cm */
797a9f9c 528 i_tags_set(&im->tags, "jpeg_density_unit_name", "centimeter", -1);
6d54291b
TC
529 xres *= 2.54;
530 yres *= 2.54;
531 break;
532 }
533 i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
534 i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
535 }
536
92e9df65
TC
537 /* I originally used jpeg_has_multiple_scans() here, but that can
538 * return true for non-progressive files too. The progressive_mode
539 * member is available at least as far back as 6b and does the right
540 * thing.
541 */
542 i_tags_setn(&im->tags, "jpeg_progressive",
543 cinfo.progressive_mode ? 1 : 0);
544
02d1d628
AMH
545 (void) jpeg_finish_decompress(&cinfo);
546 jpeg_destroy_decompress(&cinfo);
2c2c832a 547
797a9f9c 548 i_tags_set(&im->tags, "i_format", "jpeg", 4);
2c2c832a 549
5e84d110 550 mm_log((1,"i_readjpeg_wiol -> (%p)\n",im));
02d1d628
AMH
551 return im;
552}
553
f873cb01
TC
554/*
555=item i_writejpeg_wiol(im, ig, qfactor)
02d1d628 556
f873cb01
TC
557=cut
558*/
02d1d628 559
dd55acc8
AMH
560undef_int
561i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
dd55acc8
AMH
562 JSAMPLE *image_buffer;
563 int quality;
6d54291b
TC
564 int got_xres, got_yres, aspect_only, resunit;
565 double xres, yres;
566 int comment_entry;
6e4af7d4 567 int want_channels = im->channels;
92e9df65 568 int progressive = 0;
f9152a93 569 int optimize = 0;
02d1d628 570
dd55acc8 571 struct jpeg_compress_struct cinfo;
f873cb01 572 struct my_error_mgr jerr;
02d1d628 573
dd55acc8
AMH
574 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
575 int row_stride; /* physical row width in image buffer */
f873cb01 576 unsigned char * data = NULL;
6e4af7d4 577 i_color *line_buf = NULL;
02d1d628 578
dd55acc8 579 mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
02d1d628 580
f873cb01 581 i_clear_error();
02d1d628 582
8d14daab
TC
583 if (im->xsize > JPEG_DIM_MAX || im->ysize > JPEG_DIM_MAX) {
584 i_push_error(0, "image too large for JPEG");
585 return 0;
586 }
587
f873cb01 588 if (!(im->channels==1 || im->channels==3)) {
6e4af7d4 589 want_channels = im->channels - 1;
f873cb01
TC
590 }
591 quality = qfactor;
02d1d628 592
f873cb01
TC
593 cinfo.err = jpeg_std_error(&jerr.pub);
594 jerr.pub.error_exit = my_error_exit;
595 jerr.pub.output_message = my_output_message;
596
dd55acc8 597 jpeg_create_compress(&cinfo);
02d1d628 598
f873cb01
TC
599 if (setjmp(jerr.setjmp_buffer)) {
600 jpeg_destroy_compress(&cinfo);
601 if (data)
602 myfree(data);
6e4af7d4
TC
603 if (line_buf)
604 myfree(line_buf);
f873cb01
TC
605 return 0;
606 }
607
dd55acc8 608 jpeg_wiol_dest(&cinfo, ig);
02d1d628 609
dd55acc8
AMH
610 cinfo.image_width = im -> xsize; /* image width and height, in pixels */
611 cinfo.image_height = im -> ysize;
02d1d628 612
6e4af7d4 613 if (want_channels==3) {
dd55acc8
AMH
614 cinfo.input_components = 3; /* # of color components per pixel */
615 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
616 }
02d1d628 617
6e4af7d4 618 if (want_channels==1) {
dd55acc8
AMH
619 cinfo.input_components = 1; /* # of color components per pixel */
620 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */
621 }
02d1d628 622
dd55acc8
AMH
623 jpeg_set_defaults(&cinfo);
624 jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
02d1d628 625
92e9df65
TC
626 if (!i_tags_get_int(&im->tags, "jpeg_progressive", 0, &progressive))
627 progressive = 0;
628 if (progressive) {
629 jpeg_simple_progression(&cinfo);
630 }
f9152a93
TC
631 if (!i_tags_get_int(&im->tags, "jpeg_optimize", 0, &optimize))
632 optimize = 0;
633 cinfo.optimize_coding = optimize;
92e9df65 634
6d54291b
TC
635 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
636 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
637 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
638 aspect_only = 0;
639 if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
640 resunit = 1; /* per inch */
641 if (resunit < 0 || resunit > 2) /* default to inch if invalid */
642 resunit = 1;
643 if (got_xres || got_yres) {
644 if (!got_xres)
645 xres = yres;
646 else if (!got_yres)
647 yres = xres;
648 if (aspect_only)
649 resunit = 0; /* standard tags override format tags */
650 if (resunit == 2) {
651 /* convert to per cm */
652 xres /= 2.54;
653 yres /= 2.54;
654 }
655 cinfo.density_unit = resunit;
656 cinfo.X_density = (int)(xres + 0.5);
657 cinfo.Y_density = (int)(yres + 0.5);
658 }
659
dd55acc8 660 jpeg_start_compress(&cinfo, TRUE);
02d1d628 661
6d54291b 662 if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
c0f79ae6
TC
663 jpeg_write_marker(&cinfo, JPEG_COM,
664 (const JOCTET *)im->tags.tags[comment_entry].data,
6d54291b
TC
665 im->tags.tags[comment_entry].size);
666 }
667
dd55acc8 668 row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
02d1d628 669
6e4af7d4
TC
670 if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
671 && im->channels == want_channels) {
faa9b3e7
TC
672 image_buffer=im->idata;
673
674 while (cinfo.next_scanline < cinfo.image_height) {
675 /* jpeg_write_scanlines expects an array of pointers to scanlines.
676 * Here the array is only one element long, but you could pass
677 * more than one scanline at a time if that's more convenient.
678 */
679 row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
680 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
681 }
682 }
2a31a4b4
TC
683 else {
684 i_color bg;
685
686 i_get_file_background(im, &bg);
f873cb01 687 data = mymalloc(im->xsize * im->channels);
faa9b3e7
TC
688 if (data) {
689 while (cinfo.next_scanline < cinfo.image_height) {
690 /* jpeg_write_scanlines expects an array of pointers to scanlines.
691 * Here the array is only one element long, but you could pass
692 * more than one scanline at a time if that's more convenient.
693 */
2a31a4b4
TC
694 i_gsamp_bg(im, 0, im->xsize, cinfo.next_scanline, data,
695 want_channels, &bg);
faa9b3e7
TC
696 row_pointer[0] = data;
697 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
698 }
02ea5e47 699 myfree(data);
faa9b3e7
TC
700 }
701 else {
faa9b3e7 702 jpeg_destroy_compress(&cinfo);
f873cb01 703 i_push_error(0, "out of memory");
faa9b3e7
TC
704 return 0; /* out of memory? */
705 }
dd55acc8 706 }
02d1d628 707
dd55acc8 708 /* Step 6: Finish compression */
02d1d628 709
dd55acc8 710 jpeg_finish_compress(&cinfo);
02d1d628 711
dd55acc8 712 jpeg_destroy_compress(&cinfo);
02d1d628 713
6d5c85a2
TC
714 if (i_io_close(ig))
715 return 0;
10461f9a 716
dd55acc8 717 return(1);
02d1d628 718}
f873cb01
TC
719
720/*
721=back
722
723=head1 AUTHOR
724
725Arnar M. Hrafnkelsson, addi@umich.edu
726
727=head1 SEE ALSO
728
729Imager(3)
730
731=cut
732*/