4 jpeg.c - implement saving and loading JPEG images
9 if (!i_writejpeg_wiol(im, ig, quality)) {
12 im = i_readjpeg_wiol(ig, length, iptc_text, itlength);
16 Reads and writes JPEG images
39 #define JPEG_APP13 0xED /* APP13 marker code */
40 #define JPEG_APP1 (JPEG_APP0 + 1)
43 #define JPEG_DIM_MAX JPEG_MAX_DIMENSION
45 static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
47 /* Source and Destination managers */
50 struct jpeg_source_mgr pub; /* public fields */
52 JOCTET *buffer; /* start of buffer */
53 int length; /* Do I need this? */
54 boolean start_of_file; /* have we gotten any data yet? */
58 struct jpeg_destination_mgr pub; /* public fields */
60 JOCTET *buffer; /* start of buffer */
61 boolean start_of_file; /* have we gotten any data yet? */
62 } wiol_destination_mgr;
64 typedef wiol_source_mgr *wiol_src_ptr;
65 typedef wiol_destination_mgr *wiol_dest_ptr;
68 * Methods for io manager objects
70 * Methods for source managers:
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);
81 wiol_init_source (j_decompress_ptr cinfo) {
82 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
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.
88 src->start_of_file = TRUE;
94 wiol_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" */
98 mm_log((1,"wiol_fill_input_buffer(cinfo %p)\n", cinfo));
100 nbytes = i_io_read(src->data, src->buffer, JPGS);
102 if (nbytes <= 0) { /* Insert a fake EOI marker */
103 src->pub.next_input_byte = fake_eoi;
104 src->pub.bytes_in_buffer = 2;
106 src->pub.next_input_byte = src->buffer;
107 src->pub.bytes_in_buffer = nbytes;
109 src->start_of_file = FALSE;
115 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
116 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
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.
124 while (num_bytes > (long) src->pub.bytes_in_buffer) {
125 num_bytes -= (long) src->pub.bytes_in_buffer;
126 (void) wiol_fill_input_buffer(cinfo);
127 /* note we assume that fill_input_buffer will never return FALSE,
128 * so suspension need not be handled.
131 src->pub.next_input_byte += (size_t) num_bytes;
132 src->pub.bytes_in_buffer -= (size_t) num_bytes;
137 wiol_term_source (j_decompress_ptr cinfo) {
138 /* no work necessary here */
140 if (cinfo->src != NULL) {
141 src = (wiol_src_ptr) cinfo->src;
147 /* Source manager Constructor */
150 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
153 if (cinfo->src == NULL) { /* first time for this JPEG object? */
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;
159 /* put the request method call in here later */
161 src = (wiol_src_ptr) cinfo->src;
163 src->buffer = mymalloc( JPGS );
164 src->length = length;
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;
169 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
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 */
179 * Methods for destination managers:
181 * init_destination (j_compress_ptr cinfo);
182 * empty_output_buffer (j_compress_ptr cinfo);
183 * term_destination (j_compress_ptr cinfo);
188 wiol_init_destination (j_compress_ptr cinfo) {
189 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
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.
195 dest->start_of_file = TRUE;
199 wiol_empty_output_buffer(j_compress_ptr cinfo) {
200 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
203 Previously this code was checking free_in_buffer to see how much
204 needed to be written. This does not follow the documentation:
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)."
210 ssize_t nbytes = JPGS - dest->pub.free_in_buffer;
213 mm_log((1,"wiol_empty_output_buffer(cinfo %p)\n", cinfo));
214 rc = i_io_write(dest->data, dest->buffer, JPGS);
216 if (rc != JPGS) { /* XXX: Should raise some jpeg error */
217 myfree(dest->buffer);
218 mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, rc));
219 ERREXIT(cinfo, JERR_FILE_WRITE);
221 dest->pub.free_in_buffer = JPGS;
222 dest->pub.next_output_byte = dest->buffer;
227 wiol_term_destination (j_compress_ptr cinfo) {
228 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
229 size_t nbytes = JPGS - dest->pub.free_in_buffer;
230 /* yes, this needs to flush the buffer */
231 /* needs error handling */
233 if (i_io_write(dest->data, dest->buffer, nbytes) != nbytes) {
234 myfree(dest->buffer);
235 ERREXIT(cinfo, JERR_FILE_WRITE);
238 if (dest != NULL) myfree(dest->buffer);
242 /* Destination manager Constructor */
245 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
248 if (cinfo->dest == NULL) { /* first time for this JPEG object? */
250 (struct jpeg_destination_mgr *)
251 (*cinfo->mem->alloc_small)
252 ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
255 dest = (wiol_dest_ptr) cinfo->dest;
257 dest->buffer = mymalloc( JPGS );
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;
267 my_output_message (j_common_ptr cinfo) {
268 char buffer[JMSG_LENGTH_MAX];
270 /* Create the message */
271 (*cinfo->err->format_message) (cinfo, buffer);
273 i_push_error(0, buffer);
275 /* Send it to stderr, adding a newline */
276 mm_log((1, "%s\n", buffer));
279 struct my_error_mgr {
280 struct jpeg_error_mgr pub; /* "public" fields */
281 jmp_buf setjmp_buffer; /* for return to caller */
284 typedef struct my_error_mgr * my_error_ptr;
286 /* Here's the routine that will replace the standard error_exit method */
289 my_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;
293 /* Always display the message. */
294 /* We could postpone this until after returning, if we chose. */
295 (*cinfo->err->output_message) (cinfo);
297 /* Return control to the setjmp point */
298 longjmp(myerr->setjmp_buffer, 1);
302 transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
303 JSAMPROW inrow = *in;
305 /* extract and convert to real CMYK */
306 /* horribly enough this is correct given cmyk values are inverted */
311 out->rgba.r = (c * k) / MAXJSAMPLE;
312 out->rgba.g = (m * k) / MAXJSAMPLE;
313 out->rgba.b = (y * k) / MAXJSAMPLE;
319 transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
320 JSAMPROW inrow = *in;
322 out->rgba.r = *inrow++;
323 out->rgba.g = *inrow++;
324 out->rgba.b = *inrow++;
330 transfer_gray(i_color *out, JSAMPARRAY in, int width) {
331 JSAMPROW inrow = *in;
333 out->gray.gray_color = *inrow++;
338 typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
341 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
346 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
347 i_img * volatile im = NULL;
349 i_color * volatile line_buffer = NULL;
350 struct jpeg_decompress_struct cinfo;
351 struct my_error_mgr jerr;
352 JSAMPARRAY buffer; /* Output row buffer */
353 int row_stride; /* physical row width in output buffer */
354 jpeg_saved_marker_ptr markerp;
355 transfer_function_t transfer_f;
357 volatile int src_set = 0;
359 mm_log((1,"i_readjpeg_wiol(data %p, length %d,iptc_itext %p)\n", data, length, iptc_itext));
366 cinfo.err = jpeg_std_error(&jerr.pub);
367 jerr.pub.error_exit = my_error_exit;
368 jerr.pub.output_message = my_output_message;
370 /* Set error handler */
371 if (setjmp(jerr.setjmp_buffer)) {
373 wiol_term_source(&cinfo);
374 jpeg_destroy_decompress(&cinfo);
382 jpeg_create_decompress(&cinfo);
383 jpeg_save_markers(&cinfo, JPEG_APP13, 0xFFFF);
384 jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
385 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
386 jpeg_wiol_src(&cinfo, data, length);
389 (void) jpeg_read_header(&cinfo, TRUE);
390 (void) jpeg_start_decompress(&cinfo);
392 channels = cinfo.output_components;
393 switch (cinfo.out_color_space) {
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);
402 transfer_f = transfer_gray;
406 transfer_f = transfer_rgb;
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);
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.
421 Is there any app that still produces correct CMYK JPEGs?
423 transfer_f = transfer_cmyk_inverted;
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);
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);
443 if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
444 channels, sizeof(i_sample_t))) {
445 mm_log((1, "i_readjpeg: image size exceeds limits\n"));
446 wiol_term_source(&cinfo);
447 jpeg_destroy_decompress(&cinfo);
451 im = i_img_8_new(cinfo.output_width, cinfo.output_height, channels);
453 wiol_term_source(&cinfo);
454 jpeg_destroy_decompress(&cinfo);
457 row_stride = cinfo.output_width * cinfo.output_components;
458 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
459 line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
460 while (cinfo.output_scanline < cinfo.output_height) {
461 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
462 transfer_f(line_buffer, buffer, cinfo.output_width);
463 i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
468 /* check for APP1 marker and save */
469 markerp = cinfo.marker_list;
470 while (markerp != NULL) {
471 if (markerp->marker == JPEG_COM) {
472 i_tags_set(&im->tags, "jpeg_comment", (const char *)markerp->data,
473 markerp->data_length);
475 else if (markerp->marker == JPEG_APP1 && !seen_exif) {
476 seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
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;
484 markerp = markerp->next;
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);
490 if (cinfo.saw_JFIF_marker) {
491 double xres = cinfo.X_density;
492 double yres = cinfo.Y_density;
494 i_tags_setn(&im->tags, "jpeg_density_unit", cinfo.density_unit);
495 switch (cinfo.density_unit) {
496 case 0: /* values are just the aspect ratio */
497 i_tags_setn(&im->tags, "i_aspect_only", 1);
498 i_tags_set(&im->tags, "jpeg_density_unit_name", "none", -1);
501 case 1: /* per inch */
502 i_tags_set(&im->tags, "jpeg_density_unit_name", "inch", -1);
506 i_tags_set(&im->tags, "jpeg_density_unit_name", "centimeter", -1);
511 i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
512 i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
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
520 i_tags_setn(&im->tags, "jpeg_progressive",
521 cinfo.progressive_mode ? 1 : 0);
523 (void) jpeg_finish_decompress(&cinfo);
524 jpeg_destroy_decompress(&cinfo);
526 i_tags_set(&im->tags, "i_format", "jpeg", 4);
528 mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
533 =item i_writejpeg_wiol(im, ig, qfactor)
539 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
540 JSAMPLE *image_buffer;
542 int got_xres, got_yres, aspect_only, resunit;
545 int want_channels = im->channels;
548 struct jpeg_compress_struct cinfo;
549 struct my_error_mgr jerr;
551 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
552 int row_stride; /* physical row width in image buffer */
553 unsigned char * data = NULL;
554 i_color *line_buf = NULL;
556 mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
560 if (im->xsize > JPEG_DIM_MAX || im->ysize > JPEG_DIM_MAX) {
561 i_push_error(0, "image too large for JPEG");
565 if (!(im->channels==1 || im->channels==3)) {
566 want_channels = im->channels - 1;
570 cinfo.err = jpeg_std_error(&jerr.pub);
571 jerr.pub.error_exit = my_error_exit;
572 jerr.pub.output_message = my_output_message;
574 jpeg_create_compress(&cinfo);
576 if (setjmp(jerr.setjmp_buffer)) {
577 jpeg_destroy_compress(&cinfo);
585 jpeg_wiol_dest(&cinfo, ig);
587 cinfo.image_width = im -> xsize; /* image width and height, in pixels */
588 cinfo.image_height = im -> ysize;
590 if (want_channels==3) {
591 cinfo.input_components = 3; /* # of color components per pixel */
592 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
595 if (want_channels==1) {
596 cinfo.input_components = 1; /* # of color components per pixel */
597 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */
600 jpeg_set_defaults(&cinfo);
601 jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
603 if (!i_tags_get_int(&im->tags, "jpeg_progressive", 0, &progressive))
606 jpeg_simple_progression(&cinfo);
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))
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 */
617 if (got_xres || got_yres) {
623 resunit = 0; /* standard tags override format tags */
625 /* convert to per cm */
629 cinfo.density_unit = resunit;
630 cinfo.X_density = (int)(xres + 0.5);
631 cinfo.Y_density = (int)(yres + 0.5);
634 jpeg_start_compress(&cinfo, TRUE);
636 if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
637 jpeg_write_marker(&cinfo, JPEG_COM,
638 (const JOCTET *)im->tags.tags[comment_entry].data,
639 im->tags.tags[comment_entry].size);
642 row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
644 if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
645 && im->channels == want_channels) {
646 image_buffer=im->idata;
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.
653 row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
654 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
660 i_get_file_background(im, &bg);
661 data = mymalloc(im->xsize * im->channels);
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.
668 i_gsamp_bg(im, 0, im->xsize, cinfo.next_scanline, data,
670 row_pointer[0] = data;
671 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
676 jpeg_destroy_compress(&cinfo);
677 i_push_error(0, "out of memory");
678 return 0; /* out of memory? */
682 /* Step 6: Finish compression */
684 jpeg_finish_compress(&cinfo);
686 jpeg_destroy_compress(&cinfo);
699 Arnar M. Hrafnkelsson, addi@umich.edu