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 #define _STRINGIFY(x) #x
46 #define STRINGIFY(x) _STRINGIFY(x)
48 static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
50 /* Source and Destination managers */
53 struct jpeg_source_mgr pub; /* public fields */
55 JOCTET *buffer; /* start of buffer */
56 int length; /* Do I need this? */
57 boolean start_of_file; /* have we gotten any data yet? */
61 struct jpeg_destination_mgr pub; /* public fields */
63 JOCTET *buffer; /* start of buffer */
64 boolean start_of_file; /* have we gotten any data yet? */
65 } wiol_destination_mgr;
67 typedef wiol_source_mgr *wiol_src_ptr;
68 typedef wiol_destination_mgr *wiol_dest_ptr;
71 * Methods for io manager objects
73 * Methods for source managers:
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);
84 wiol_init_source (j_decompress_ptr cinfo) {
85 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
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.
91 src->start_of_file = TRUE;
97 wiol_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" */
101 mm_log((1,"wiol_fill_input_buffer(cinfo %p)\n", cinfo));
103 nbytes = i_io_read(src->data, src->buffer, JPGS);
105 if (nbytes <= 0) { /* Insert a fake EOI marker */
106 src->pub.next_input_byte = fake_eoi;
107 src->pub.bytes_in_buffer = 2;
109 src->pub.next_input_byte = src->buffer;
110 src->pub.bytes_in_buffer = nbytes;
112 src->start_of_file = FALSE;
118 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
119 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
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.
127 while (num_bytes > (long) src->pub.bytes_in_buffer) {
128 num_bytes -= (long) src->pub.bytes_in_buffer;
129 (void) wiol_fill_input_buffer(cinfo);
130 /* note we assume that fill_input_buffer will never return FALSE,
131 * so suspension need not be handled.
134 src->pub.next_input_byte += (size_t) num_bytes;
135 src->pub.bytes_in_buffer -= (size_t) num_bytes;
140 wiol_term_source (j_decompress_ptr cinfo) {
141 /* no work necessary here */
143 if (cinfo->src != NULL) {
144 src = (wiol_src_ptr) cinfo->src;
150 /* Source manager Constructor */
153 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
156 if (cinfo->src == NULL) { /* first time for this JPEG object? */
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;
162 /* put the request method call in here later */
164 src = (wiol_src_ptr) cinfo->src;
166 src->buffer = mymalloc( JPGS );
167 src->length = length;
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;
172 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
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 */
182 * Methods for destination managers:
184 * init_destination (j_compress_ptr cinfo);
185 * empty_output_buffer (j_compress_ptr cinfo);
186 * term_destination (j_compress_ptr cinfo);
191 wiol_init_destination (j_compress_ptr cinfo) {
192 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
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.
198 dest->start_of_file = TRUE;
202 wiol_empty_output_buffer(j_compress_ptr cinfo) {
203 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
206 Previously this code was checking free_in_buffer to see how much
207 needed to be written. This does not follow the documentation:
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)."
213 ssize_t nbytes = JPGS - dest->pub.free_in_buffer;
216 mm_log((1,"wiol_empty_output_buffer(cinfo %p)\n", cinfo));
217 rc = i_io_write(dest->data, dest->buffer, JPGS);
219 if (rc != JPGS) { /* XXX: Should raise some jpeg error */
220 myfree(dest->buffer);
221 mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, (int)rc));
222 ERREXIT(cinfo, JERR_FILE_WRITE);
224 dest->pub.free_in_buffer = JPGS;
225 dest->pub.next_output_byte = dest->buffer;
230 wiol_term_destination (j_compress_ptr cinfo) {
231 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
232 size_t nbytes = JPGS - dest->pub.free_in_buffer;
233 /* yes, this needs to flush the buffer */
234 /* needs error handling */
236 if (i_io_write(dest->data, dest->buffer, nbytes) != nbytes) {
237 myfree(dest->buffer);
238 ERREXIT(cinfo, JERR_FILE_WRITE);
241 if (dest != NULL) myfree(dest->buffer);
245 /* Destination manager Constructor */
248 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
251 if (cinfo->dest == NULL) { /* first time for this JPEG object? */
253 (struct jpeg_destination_mgr *)
254 (*cinfo->mem->alloc_small)
255 ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
258 dest = (wiol_dest_ptr) cinfo->dest;
260 dest->buffer = mymalloc( JPGS );
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;
270 my_output_message (j_common_ptr cinfo) {
271 char buffer[JMSG_LENGTH_MAX];
273 /* Create the message */
274 (*cinfo->err->format_message) (cinfo, buffer);
276 i_push_error(0, buffer);
278 /* Send it to stderr, adding a newline */
279 mm_log((1, "%s\n", buffer));
282 struct my_error_mgr {
283 struct jpeg_error_mgr pub; /* "public" fields */
284 jmp_buf setjmp_buffer; /* for return to caller */
287 typedef struct my_error_mgr * my_error_ptr;
289 /* Here's the routine that will replace the standard error_exit method */
292 my_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;
296 /* Always display the message. */
297 /* We could postpone this until after returning, if we chose. */
298 (*cinfo->err->output_message) (cinfo);
300 /* Return control to the setjmp point */
301 longjmp(myerr->setjmp_buffer, 1);
305 transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
306 JSAMPROW inrow = *in;
308 /* extract and convert to real CMYK */
309 /* horribly enough this is correct given cmyk values are inverted */
314 out->rgba.r = (c * k) / MAXJSAMPLE;
315 out->rgba.g = (m * k) / MAXJSAMPLE;
316 out->rgba.b = (y * k) / MAXJSAMPLE;
322 transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
323 JSAMPROW inrow = *in;
325 out->rgba.r = *inrow++;
326 out->rgba.g = *inrow++;
327 out->rgba.b = *inrow++;
333 transfer_gray(i_color *out, JSAMPARRAY in, int width) {
334 JSAMPROW inrow = *in;
336 out->gray.gray_color = *inrow++;
341 typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
343 static const char version_string[] =
344 #ifdef LIBJPEG_TURBO_VERSION
345 "libjpeg-turbo " STRINGIFY(LIBJPEG_TURBO_VERSION) " api " STRINGIFY(JPEG_LIB_VERSION)
347 "libjpeg " STRINGIFY(JPEG_LIB_VERSION)
352 =item i_libjpeg_version()
358 i_libjpeg_version(void) {
359 return version_string;
363 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
368 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
369 i_img * volatile im = NULL;
371 i_color * volatile line_buffer = NULL;
372 struct jpeg_decompress_struct cinfo;
373 struct my_error_mgr jerr;
374 JSAMPARRAY buffer; /* Output row buffer */
375 int row_stride; /* physical row width in output buffer */
376 jpeg_saved_marker_ptr markerp;
377 transfer_function_t transfer_f;
379 volatile int src_set = 0;
381 mm_log((1,"i_readjpeg_wiol(data %p, length %d,iptc_itext %p)\n", data, length, iptc_itext));
388 cinfo.err = jpeg_std_error(&jerr.pub);
389 jerr.pub.error_exit = my_error_exit;
390 jerr.pub.output_message = my_output_message;
392 /* Set error handler */
393 if (setjmp(jerr.setjmp_buffer)) {
395 wiol_term_source(&cinfo);
396 jpeg_destroy_decompress(&cinfo);
404 jpeg_create_decompress(&cinfo);
405 jpeg_save_markers(&cinfo, JPEG_APP13, 0xFFFF);
406 jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
407 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
408 jpeg_wiol_src(&cinfo, data, length);
411 (void) jpeg_read_header(&cinfo, TRUE);
412 (void) jpeg_start_decompress(&cinfo);
414 channels = cinfo.output_components;
415 switch (cinfo.out_color_space) {
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);
424 transfer_f = transfer_gray;
428 transfer_f = transfer_rgb;
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);
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.
443 Is there any app that still produces correct CMYK JPEGs?
445 transfer_f = transfer_cmyk_inverted;
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);
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);
465 if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
466 channels, sizeof(i_sample_t))) {
467 mm_log((1, "i_readjpeg: image size exceeds limits\n"));
468 wiol_term_source(&cinfo);
469 jpeg_destroy_decompress(&cinfo);
473 im = i_img_8_new(cinfo.output_width, cinfo.output_height, channels);
475 wiol_term_source(&cinfo);
476 jpeg_destroy_decompress(&cinfo);
479 row_stride = cinfo.output_width * cinfo.output_components;
480 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
481 line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
482 while (cinfo.output_scanline < cinfo.output_height) {
483 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
484 transfer_f(line_buffer, buffer, cinfo.output_width);
485 i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
490 /* check for APP1 marker and save */
491 markerp = cinfo.marker_list;
492 while (markerp != NULL) {
493 if (markerp->marker == JPEG_COM) {
494 i_tags_set(&im->tags, "jpeg_comment", (const char *)markerp->data,
495 markerp->data_length);
497 else if (markerp->marker == JPEG_APP1 && !seen_exif) {
498 seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
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;
506 markerp = markerp->next;
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);
512 if (cinfo.saw_JFIF_marker) {
513 double xres = cinfo.X_density;
514 double yres = cinfo.Y_density;
516 i_tags_setn(&im->tags, "jpeg_density_unit", cinfo.density_unit);
517 switch (cinfo.density_unit) {
518 case 0: /* values are just the aspect ratio */
519 i_tags_setn(&im->tags, "i_aspect_only", 1);
520 i_tags_set(&im->tags, "jpeg_density_unit_name", "none", -1);
523 case 1: /* per inch */
524 i_tags_set(&im->tags, "jpeg_density_unit_name", "inch", -1);
528 i_tags_set(&im->tags, "jpeg_density_unit_name", "centimeter", -1);
533 i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
534 i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
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
542 i_tags_setn(&im->tags, "jpeg_progressive",
543 cinfo.progressive_mode ? 1 : 0);
545 (void) jpeg_finish_decompress(&cinfo);
546 jpeg_destroy_decompress(&cinfo);
548 i_tags_set(&im->tags, "i_format", "jpeg", 4);
550 mm_log((1,"i_readjpeg_wiol -> (%p)\n",im));
555 =item i_writejpeg_wiol(im, ig, qfactor)
561 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
562 JSAMPLE *image_buffer;
564 int got_xres, got_yres, aspect_only, resunit;
567 int want_channels = im->channels;
570 struct jpeg_compress_struct cinfo;
571 struct my_error_mgr jerr;
573 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
574 int row_stride; /* physical row width in image buffer */
575 unsigned char * data = NULL;
576 i_color *line_buf = NULL;
578 mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
582 if (im->xsize > JPEG_DIM_MAX || im->ysize > JPEG_DIM_MAX) {
583 i_push_error(0, "image too large for JPEG");
587 if (!(im->channels==1 || im->channels==3)) {
588 want_channels = im->channels - 1;
592 cinfo.err = jpeg_std_error(&jerr.pub);
593 jerr.pub.error_exit = my_error_exit;
594 jerr.pub.output_message = my_output_message;
596 jpeg_create_compress(&cinfo);
598 if (setjmp(jerr.setjmp_buffer)) {
599 jpeg_destroy_compress(&cinfo);
607 jpeg_wiol_dest(&cinfo, ig);
609 cinfo.image_width = im -> xsize; /* image width and height, in pixels */
610 cinfo.image_height = im -> ysize;
612 if (want_channels==3) {
613 cinfo.input_components = 3; /* # of color components per pixel */
614 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
617 if (want_channels==1) {
618 cinfo.input_components = 1; /* # of color components per pixel */
619 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */
622 jpeg_set_defaults(&cinfo);
623 jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
625 if (!i_tags_get_int(&im->tags, "jpeg_progressive", 0, &progressive))
628 jpeg_simple_progression(&cinfo);
631 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
632 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
633 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
635 if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
636 resunit = 1; /* per inch */
637 if (resunit < 0 || resunit > 2) /* default to inch if invalid */
639 if (got_xres || got_yres) {
645 resunit = 0; /* standard tags override format tags */
647 /* convert to per cm */
651 cinfo.density_unit = resunit;
652 cinfo.X_density = (int)(xres + 0.5);
653 cinfo.Y_density = (int)(yres + 0.5);
656 jpeg_start_compress(&cinfo, TRUE);
658 if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
659 jpeg_write_marker(&cinfo, JPEG_COM,
660 (const JOCTET *)im->tags.tags[comment_entry].data,
661 im->tags.tags[comment_entry].size);
664 row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
666 if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
667 && im->channels == want_channels) {
668 image_buffer=im->idata;
670 while (cinfo.next_scanline < cinfo.image_height) {
671 /* jpeg_write_scanlines expects an array of pointers to scanlines.
672 * Here the array is only one element long, but you could pass
673 * more than one scanline at a time if that's more convenient.
675 row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
676 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
682 i_get_file_background(im, &bg);
683 data = mymalloc(im->xsize * im->channels);
685 while (cinfo.next_scanline < cinfo.image_height) {
686 /* jpeg_write_scanlines expects an array of pointers to scanlines.
687 * Here the array is only one element long, but you could pass
688 * more than one scanline at a time if that's more convenient.
690 i_gsamp_bg(im, 0, im->xsize, cinfo.next_scanline, data,
692 row_pointer[0] = data;
693 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
698 jpeg_destroy_compress(&cinfo);
699 i_push_error(0, "out of memory");
700 return 0; /* out of memory? */
704 /* Step 6: Finish compression */
706 jpeg_finish_compress(&cinfo);
708 jpeg_destroy_compress(&cinfo);
721 Arnar M. Hrafnkelsson, addi@umich.edu