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