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
38 #define JPEG_APP13 0xED /* APP13 marker code */
39 #define JPEG_APP1 (JPEG_APP0 + 1)
42 #define JPEG_DIM_MAX JPEG_MAX_DIMENSION
44 #define _STRINGIFY(x) #x
45 #define STRINGIFY(x) _STRINGIFY(x)
47 static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
49 /* Source and Destination managers */
52 struct jpeg_source_mgr pub; /* public fields */
54 JOCTET *buffer; /* start of buffer */
55 int length; /* Do I need this? */
56 boolean start_of_file; /* have we gotten any data yet? */
60 struct jpeg_destination_mgr pub; /* public fields */
62 JOCTET *buffer; /* start of buffer */
63 boolean start_of_file; /* have we gotten any data yet? */
64 } wiol_destination_mgr;
66 typedef wiol_source_mgr *wiol_src_ptr;
67 typedef wiol_destination_mgr *wiol_dest_ptr;
70 * Methods for io manager objects
72 * Methods for source managers:
74 * init_source (j_decompress_ptr cinfo);
75 * skip_input_data (j_decompress_ptr cinfo, long num_bytes);
76 * fill_input_buffer (j_decompress_ptr cinfo);
77 * term_source (j_decompress_ptr cinfo);
83 wiol_init_source (j_decompress_ptr cinfo) {
84 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
86 /* We reset the empty-input-file flag for each image, but we don't clear
87 * the input buffer. This is correct behavior for reading a series of
88 * images from one source.
90 src->start_of_file = TRUE;
96 wiol_fill_input_buffer(j_decompress_ptr cinfo) {
97 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
98 ssize_t nbytes; /* We assume that reads are "small" */
100 mm_log((1,"wiol_fill_input_buffer(cinfo %p)\n", cinfo));
102 nbytes = i_io_read(src->data, src->buffer, JPGS);
104 if (nbytes <= 0) { /* Insert a fake EOI marker */
105 src->pub.next_input_byte = fake_eoi;
106 src->pub.bytes_in_buffer = 2;
108 src->pub.next_input_byte = src->buffer;
109 src->pub.bytes_in_buffer = nbytes;
111 src->start_of_file = FALSE;
117 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
118 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
120 /* Just a dumb implementation for now. Could use fseek() except
121 * it doesn't work on pipes. Not clear that being smart is worth
122 * any trouble anyway --- large skips are infrequent.
126 while (num_bytes > (long) src->pub.bytes_in_buffer) {
127 num_bytes -= (long) src->pub.bytes_in_buffer;
128 (void) wiol_fill_input_buffer(cinfo);
129 /* note we assume that fill_input_buffer will never return FALSE,
130 * so suspension need not be handled.
133 src->pub.next_input_byte += (size_t) num_bytes;
134 src->pub.bytes_in_buffer -= (size_t) num_bytes;
139 wiol_term_source (j_decompress_ptr cinfo) {
140 /* no work necessary here */
142 if (cinfo->src != NULL) {
143 src = (wiol_src_ptr) cinfo->src;
149 /* Source manager Constructor */
152 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
155 if (cinfo->src == NULL) { /* first time for this JPEG object? */
156 cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
157 ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(wiol_source_mgr));
158 src = (wiol_src_ptr) cinfo->src;
161 /* put the request method call in here later */
163 src = (wiol_src_ptr) cinfo->src;
165 src->buffer = mymalloc( JPGS );
166 src->length = length;
168 src->pub.init_source = wiol_init_source;
169 src->pub.fill_input_buffer = wiol_fill_input_buffer;
170 src->pub.skip_input_data = wiol_skip_input_data;
171 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
172 src->pub.term_source = wiol_term_source;
173 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
174 src->pub.next_input_byte = NULL; /* until buffer loaded */
181 * Methods for destination managers:
183 * init_destination (j_compress_ptr cinfo);
184 * empty_output_buffer (j_compress_ptr cinfo);
185 * term_destination (j_compress_ptr cinfo);
190 wiol_init_destination (j_compress_ptr cinfo) {
191 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
193 /* We reset the empty-input-file flag for each image, but we don't clear
194 * the input buffer. This is correct behavior for reading a series of
195 * images from one source.
197 dest->start_of_file = TRUE;
201 wiol_empty_output_buffer(j_compress_ptr cinfo) {
202 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
205 Previously this code was checking free_in_buffer to see how much
206 needed to be written. This does not follow the documentation:
208 "In typical applications, it should write out the
209 *entire* buffer (use the saved start address and buffer length;
210 ignore the current state of next_output_byte and free_in_buffer)."
212 ssize_t nbytes = JPGS - dest->pub.free_in_buffer;
215 mm_log((1,"wiol_empty_output_buffer(cinfo %p)\n", cinfo));
216 rc = i_io_write(dest->data, dest->buffer, JPGS);
218 if (rc != JPGS) { /* XXX: Should raise some jpeg error */
219 myfree(dest->buffer);
220 mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, (int)rc));
221 ERREXIT(cinfo, JERR_FILE_WRITE);
223 dest->pub.free_in_buffer = JPGS;
224 dest->pub.next_output_byte = dest->buffer;
229 wiol_term_destination (j_compress_ptr cinfo) {
230 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
231 size_t nbytes = JPGS - dest->pub.free_in_buffer;
232 /* yes, this needs to flush the buffer */
233 /* needs error handling */
235 if (i_io_write(dest->data, dest->buffer, nbytes) != nbytes) {
236 myfree(dest->buffer);
237 ERREXIT(cinfo, JERR_FILE_WRITE);
240 if (dest != NULL) myfree(dest->buffer);
244 /* Destination manager Constructor */
247 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
250 if (cinfo->dest == NULL) { /* first time for this JPEG object? */
252 (struct jpeg_destination_mgr *)
253 (*cinfo->mem->alloc_small)
254 ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
257 dest = (wiol_dest_ptr) cinfo->dest;
259 dest->buffer = mymalloc( JPGS );
261 dest->pub.init_destination = wiol_init_destination;
262 dest->pub.empty_output_buffer = wiol_empty_output_buffer;
263 dest->pub.term_destination = wiol_term_destination;
264 dest->pub.free_in_buffer = JPGS;
265 dest->pub.next_output_byte = dest->buffer;
269 my_output_message (j_common_ptr cinfo) {
270 char buffer[JMSG_LENGTH_MAX];
272 /* Create the message */
273 (*cinfo->err->format_message) (cinfo, buffer);
275 i_push_error(0, buffer);
277 /* Send it to stderr, adding a newline */
278 mm_log((1, "%s\n", buffer));
281 struct my_error_mgr {
282 struct jpeg_error_mgr pub; /* "public" fields */
283 jmp_buf setjmp_buffer; /* for return to caller */
286 typedef struct my_error_mgr * my_error_ptr;
288 /* Here's the routine that will replace the standard error_exit method */
291 my_error_exit (j_common_ptr cinfo) {
292 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
293 my_error_ptr myerr = (my_error_ptr) cinfo->err;
295 /* Always display the message. */
296 /* We could postpone this until after returning, if we chose. */
297 (*cinfo->err->output_message) (cinfo);
299 /* Return control to the setjmp point */
300 longjmp(myerr->setjmp_buffer, 1);
304 transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
305 JSAMPROW inrow = *in;
307 /* extract and convert to real CMYK */
308 /* horribly enough this is correct given cmyk values are inverted */
313 out->rgba.r = (c * k) / MAXJSAMPLE;
314 out->rgba.g = (m * k) / MAXJSAMPLE;
315 out->rgba.b = (y * k) / MAXJSAMPLE;
321 transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
322 JSAMPROW inrow = *in;
324 out->rgba.r = *inrow++;
325 out->rgba.g = *inrow++;
326 out->rgba.b = *inrow++;
332 transfer_gray(i_color *out, JSAMPARRAY in, int width) {
333 JSAMPROW inrow = *in;
335 out->gray.gray_color = *inrow++;
340 typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
342 static const char version_string[] =
343 #ifdef LIBJPEG_TURBO_VERSION
344 "libjpeg-turbo " STRINGIFY(LIBJPEG_TURBO_VERSION) " api " STRINGIFY(JPEG_LIB_VERSION)
346 "libjpeg " STRINGIFY(JPEG_LIB_VERSION)
351 =item i_libjpeg_version()
357 i_libjpeg_version(void) {
358 return version_string;
362 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
367 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
368 i_img * volatile im = NULL;
370 i_color * volatile line_buffer = NULL;
371 struct jpeg_decompress_struct cinfo;
372 struct my_error_mgr jerr;
373 JSAMPARRAY buffer; /* Output row buffer */
374 int row_stride; /* physical row width in output buffer */
375 jpeg_saved_marker_ptr markerp;
376 transfer_function_t transfer_f;
378 volatile int src_set = 0;
380 mm_log((1,"i_readjpeg_wiol(data %p, length %d,iptc_itext %p)\n", data, length, iptc_itext));
387 cinfo.err = jpeg_std_error(&jerr.pub);
388 jerr.pub.error_exit = my_error_exit;
389 jerr.pub.output_message = my_output_message;
391 /* Set error handler */
392 if (setjmp(jerr.setjmp_buffer)) {
394 wiol_term_source(&cinfo);
395 jpeg_destroy_decompress(&cinfo);
403 jpeg_create_decompress(&cinfo);
404 jpeg_save_markers(&cinfo, JPEG_APP13, 0xFFFF);
405 jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
406 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
407 jpeg_wiol_src(&cinfo, data, length);
410 (void) jpeg_read_header(&cinfo, TRUE);
411 (void) jpeg_start_decompress(&cinfo);
413 channels = cinfo.output_components;
414 switch (cinfo.out_color_space) {
416 if (cinfo.output_components != 1) {
417 mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components));
418 i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components);
419 wiol_term_source(&cinfo);
420 jpeg_destroy_decompress(&cinfo);
423 transfer_f = transfer_gray;
427 transfer_f = transfer_rgb;
428 if (cinfo.output_components != 3) {
429 mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components));
430 i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components);
431 wiol_term_source(&cinfo);
432 jpeg_destroy_decompress(&cinfo);
438 if (cinfo.output_components == 4) {
439 /* we treat the CMYK values as inverted, because that's what that
440 buggy photoshop does, and everyone has to follow the gorilla.
442 Is there any app that still produces correct CMYK JPEGs?
444 transfer_f = transfer_cmyk_inverted;
448 mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components));
449 i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components);
450 wiol_term_source(&cinfo);
451 jpeg_destroy_decompress(&cinfo);
457 mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space));
458 i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space);
459 wiol_term_source(&cinfo);
460 jpeg_destroy_decompress(&cinfo);
464 if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
465 channels, sizeof(i_sample_t))) {
466 mm_log((1, "i_readjpeg: image size exceeds limits\n"));
467 wiol_term_source(&cinfo);
468 jpeg_destroy_decompress(&cinfo);
472 im = i_img_8_new(cinfo.output_width, cinfo.output_height, channels);
474 wiol_term_source(&cinfo);
475 jpeg_destroy_decompress(&cinfo);
478 row_stride = cinfo.output_width * cinfo.output_components;
479 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
480 line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
481 while (cinfo.output_scanline < cinfo.output_height) {
482 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
483 transfer_f(line_buffer, buffer, cinfo.output_width);
484 i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
489 /* check for APP1 marker and save */
490 markerp = cinfo.marker_list;
491 while (markerp != NULL) {
492 if (markerp->marker == JPEG_COM) {
493 i_tags_set(&im->tags, "jpeg_comment", (const char *)markerp->data,
494 markerp->data_length);
496 else if (markerp->marker == JPEG_APP1 && !seen_exif) {
497 unsigned char *data = markerp->data;
498 size_t len = markerp->data_length;
499 if (len >= 6 && memcmp(data, "Exif\0\0", 6) == 0) {
500 seen_exif = im_decode_exif(im, data+6, len-6);
503 else if (markerp->marker == JPEG_APP13) {
504 *iptc_itext = mymalloc(markerp->data_length);
505 memcpy(*iptc_itext, markerp->data, markerp->data_length);
506 *itlength = markerp->data_length;
509 markerp = markerp->next;
512 i_tags_setn(&im->tags, "jpeg_out_color_space", cinfo.out_color_space);
513 i_tags_setn(&im->tags, "jpeg_color_space", cinfo.jpeg_color_space);
515 if (cinfo.saw_JFIF_marker) {
516 double xres = cinfo.X_density;
517 double yres = cinfo.Y_density;
519 i_tags_setn(&im->tags, "jpeg_density_unit", cinfo.density_unit);
520 switch (cinfo.density_unit) {
521 case 0: /* values are just the aspect ratio */
522 i_tags_setn(&im->tags, "i_aspect_only", 1);
523 i_tags_set(&im->tags, "jpeg_density_unit_name", "none", -1);
526 case 1: /* per inch */
527 i_tags_set(&im->tags, "jpeg_density_unit_name", "inch", -1);
531 i_tags_set(&im->tags, "jpeg_density_unit_name", "centimeter", -1);
536 i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
537 i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
540 /* I originally used jpeg_has_multiple_scans() here, but that can
541 * return true for non-progressive files too. The progressive_mode
542 * member is available at least as far back as 6b and does the right
545 i_tags_setn(&im->tags, "jpeg_progressive",
546 cinfo.progressive_mode ? 1 : 0);
548 (void) jpeg_finish_decompress(&cinfo);
549 jpeg_destroy_decompress(&cinfo);
551 i_tags_set(&im->tags, "i_format", "jpeg", 4);
553 mm_log((1,"i_readjpeg_wiol -> (%p)\n",im));
558 =item i_writejpeg_wiol(im, ig, qfactor)
564 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
565 JSAMPLE *image_buffer;
567 int got_xres, got_yres, aspect_only, resunit;
570 int want_channels = im->channels;
574 struct jpeg_compress_struct cinfo;
575 struct my_error_mgr jerr;
577 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
578 int row_stride; /* physical row width in image buffer */
579 unsigned char * data = NULL;
580 i_color *line_buf = NULL;
582 mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
586 if (im->xsize > JPEG_DIM_MAX || im->ysize > JPEG_DIM_MAX) {
587 i_push_error(0, "image too large for JPEG");
591 if (!(im->channels==1 || im->channels==3)) {
592 want_channels = im->channels - 1;
596 cinfo.err = jpeg_std_error(&jerr.pub);
597 jerr.pub.error_exit = my_error_exit;
598 jerr.pub.output_message = my_output_message;
600 jpeg_create_compress(&cinfo);
602 if (setjmp(jerr.setjmp_buffer)) {
603 jpeg_destroy_compress(&cinfo);
611 jpeg_wiol_dest(&cinfo, ig);
613 cinfo.image_width = im -> xsize; /* image width and height, in pixels */
614 cinfo.image_height = im -> ysize;
616 if (want_channels==3) {
617 cinfo.input_components = 3; /* # of color components per pixel */
618 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
621 if (want_channels==1) {
622 cinfo.input_components = 1; /* # of color components per pixel */
623 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */
626 jpeg_set_defaults(&cinfo);
627 jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
629 if (!i_tags_get_int(&im->tags, "jpeg_progressive", 0, &progressive))
632 jpeg_simple_progression(&cinfo);
634 if (!i_tags_get_int(&im->tags, "jpeg_optimize", 0, &optimize))
636 cinfo.optimize_coding = optimize;
638 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
639 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
640 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
642 if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
643 resunit = 1; /* per inch */
644 if (resunit < 0 || resunit > 2) /* default to inch if invalid */
646 if (got_xres || got_yres) {
652 resunit = 0; /* standard tags override format tags */
654 /* convert to per cm */
658 cinfo.density_unit = resunit;
659 cinfo.X_density = (int)(xres + 0.5);
660 cinfo.Y_density = (int)(yres + 0.5);
663 jpeg_start_compress(&cinfo, TRUE);
665 if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
666 jpeg_write_marker(&cinfo, JPEG_COM,
667 (const JOCTET *)im->tags.tags[comment_entry].data,
668 im->tags.tags[comment_entry].size);
671 row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
673 if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
674 && im->channels == want_channels) {
675 image_buffer=im->idata;
677 while (cinfo.next_scanline < cinfo.image_height) {
678 /* jpeg_write_scanlines expects an array of pointers to scanlines.
679 * Here the array is only one element long, but you could pass
680 * more than one scanline at a time if that's more convenient.
682 row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
683 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
689 i_get_file_background(im, &bg);
690 data = mymalloc(im->xsize * im->channels);
692 while (cinfo.next_scanline < cinfo.image_height) {
693 /* jpeg_write_scanlines expects an array of pointers to scanlines.
694 * Here the array is only one element long, but you could pass
695 * more than one scanline at a time if that's more convenient.
697 i_gsamp_bg(im, 0, im->xsize, cinfo.next_scanline, data,
699 row_pointer[0] = data;
700 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
705 jpeg_destroy_compress(&cinfo);
706 i_push_error(0, "out of memory");
707 return 0; /* out of memory? */
711 /* Step 6: Finish compression */
713 jpeg_finish_compress(&cinfo);
715 jpeg_destroy_compress(&cinfo);
728 Arnar M. Hrafnkelsson, addi@umich.edu