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 /* Bad design right here */
50 static char **iptc_text=NULL;
53 /* Source and Destination managers */
57 struct jpeg_source_mgr pub; /* public fields */
59 JOCTET *buffer; /* start of buffer */
60 int length; /* Do I need this? */
61 boolean start_of_file; /* have we gotten any data yet? */
65 struct jpeg_destination_mgr pub; /* public fields */
67 JOCTET *buffer; /* start of buffer */
68 boolean start_of_file; /* have we gotten any data yet? */
69 } wiol_destination_mgr;
71 typedef wiol_source_mgr *wiol_src_ptr;
72 typedef wiol_destination_mgr *wiol_dest_ptr;
76 * Methods for io manager objects
78 * Methods for source managers:
80 * init_source (j_decompress_ptr cinfo);
81 * skip_input_data (j_decompress_ptr cinfo, long num_bytes);
82 * fill_input_buffer (j_decompress_ptr cinfo);
83 * term_source (j_decompress_ptr cinfo);
89 wiol_init_source (j_decompress_ptr cinfo) {
90 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
92 /* We reset the empty-input-file flag for each image, but we don't clear
93 * the input buffer. This is correct behavior for reading a series of
94 * images from one source.
96 src->start_of_file = TRUE;
102 wiol_fill_input_buffer(j_decompress_ptr cinfo) {
103 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
104 ssize_t nbytes; /* We assume that reads are "small" */
106 mm_log((1,"wiol_fill_input_buffer(cinfo %p)\n", cinfo));
108 nbytes = i_io_read(src->data, src->buffer, JPGS);
110 if (nbytes <= 0) { /* Insert a fake EOI marker */
111 src->pub.next_input_byte = fake_eoi;
112 src->pub.bytes_in_buffer = 2;
114 src->pub.next_input_byte = src->buffer;
115 src->pub.bytes_in_buffer = nbytes;
117 src->start_of_file = FALSE;
123 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
124 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
126 /* Just a dumb implementation for now. Could use fseek() except
127 * it doesn't work on pipes. Not clear that being smart is worth
128 * any trouble anyway --- large skips are infrequent.
132 while (num_bytes > (long) src->pub.bytes_in_buffer) {
133 num_bytes -= (long) src->pub.bytes_in_buffer;
134 (void) wiol_fill_input_buffer(cinfo);
135 /* note we assume that fill_input_buffer will never return FALSE,
136 * so suspension need not be handled.
139 src->pub.next_input_byte += (size_t) num_bytes;
140 src->pub.bytes_in_buffer -= (size_t) num_bytes;
145 wiol_term_source (j_decompress_ptr cinfo) {
146 /* no work necessary here */
148 if (cinfo->src != NULL) {
149 src = (wiol_src_ptr) cinfo->src;
155 /* Source manager Constructor */
158 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
161 if (cinfo->src == NULL) { /* first time for this JPEG object? */
162 cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
163 ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(wiol_source_mgr));
164 src = (wiol_src_ptr) cinfo->src;
167 /* put the request method call in here later */
169 src = (wiol_src_ptr) cinfo->src;
171 src->buffer = mymalloc( JPGS );
172 src->length = length;
174 src->pub.init_source = wiol_init_source;
175 src->pub.fill_input_buffer = wiol_fill_input_buffer;
176 src->pub.skip_input_data = wiol_skip_input_data;
177 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
178 src->pub.term_source = wiol_term_source;
179 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
180 src->pub.next_input_byte = NULL; /* until buffer loaded */
187 * Methods for destination managers:
189 * init_destination (j_compress_ptr cinfo);
190 * empty_output_buffer (j_compress_ptr cinfo);
191 * term_destination (j_compress_ptr cinfo);
196 wiol_init_destination (j_compress_ptr cinfo) {
197 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
199 /* We reset the empty-input-file flag for each image, but we don't clear
200 * the input buffer. This is correct behavior for reading a series of
201 * images from one source.
203 dest->start_of_file = TRUE;
207 wiol_empty_output_buffer(j_compress_ptr cinfo) {
208 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
211 Previously this code was checking free_in_buffer to see how much
212 needed to be written. This does not follow the documentation:
214 "In typical applications, it should write out the
215 *entire* buffer (use the saved start address and buffer length;
216 ignore the current state of next_output_byte and free_in_buffer)."
218 ssize_t nbytes = JPGS - dest->pub.free_in_buffer;
221 mm_log((1,"wiol_empty_output_buffer(cinfo %p)\n", cinfo));
222 rc = i_io_write(dest->data, dest->buffer, JPGS);
224 if (rc != JPGS) { /* XXX: Should raise some jpeg error */
225 myfree(dest->buffer);
226 mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, rc));
227 ERREXIT(cinfo, JERR_FILE_WRITE);
229 dest->pub.free_in_buffer = JPGS;
230 dest->pub.next_output_byte = dest->buffer;
235 wiol_term_destination (j_compress_ptr cinfo) {
236 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
237 size_t nbytes = JPGS - dest->pub.free_in_buffer;
238 /* yes, this needs to flush the buffer */
239 /* needs error handling */
241 if (i_io_write(dest->data, dest->buffer, nbytes) != nbytes) {
242 myfree(dest->buffer);
243 ERREXIT(cinfo, JERR_FILE_WRITE);
246 if (dest != NULL) myfree(dest->buffer);
250 /* Destination manager Constructor */
253 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
256 if (cinfo->dest == NULL) { /* first time for this JPEG object? */
258 (struct jpeg_destination_mgr *)
259 (*cinfo->mem->alloc_small)
260 ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
263 dest = (wiol_dest_ptr) cinfo->dest;
265 dest->buffer = mymalloc( JPGS );
267 dest->pub.init_destination = wiol_init_destination;
268 dest->pub.empty_output_buffer = wiol_empty_output_buffer;
269 dest->pub.term_destination = wiol_term_destination;
270 dest->pub.free_in_buffer = JPGS;
271 dest->pub.next_output_byte = dest->buffer;
275 jpeg_getc (j_decompress_ptr cinfo)
278 struct jpeg_source_mgr * datasrc = cinfo->src;
280 if (datasrc->bytes_in_buffer == 0) {
281 if (! (*datasrc->fill_input_buffer) (cinfo))
282 { fprintf(stderr,"Jpeglib: cant suspend.\n"); exit(3); }
283 /* ERREXIT(cinfo, JERR_CANT_SUSPEND);*/
285 datasrc->bytes_in_buffer--;
286 return GETJOCTET(*datasrc->next_input_byte++);
290 APP13_handler (j_decompress_ptr cinfo) {
294 length = jpeg_getc(cinfo) << 8;
295 length += jpeg_getc(cinfo);
296 length -= 2; /* discount the length word itself */
300 if ( ((*iptc_text)=mymalloc(length)) == NULL ) return FALSE;
301 while (--length >= 0) (*iptc_text)[cnt++] = jpeg_getc(cinfo);
307 my_output_message (j_common_ptr cinfo) {
308 char buffer[JMSG_LENGTH_MAX];
310 /* Create the message */
311 (*cinfo->err->format_message) (cinfo, buffer);
313 i_push_error(0, buffer);
315 /* Send it to stderr, adding a newline */
316 mm_log((1, "%s\n", buffer));
319 struct my_error_mgr {
320 struct jpeg_error_mgr pub; /* "public" fields */
321 jmp_buf setjmp_buffer; /* for return to caller */
324 typedef struct my_error_mgr * my_error_ptr;
326 /* Here's the routine that will replace the standard error_exit method */
329 my_error_exit (j_common_ptr cinfo) {
330 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
331 my_error_ptr myerr = (my_error_ptr) cinfo->err;
333 /* Always display the message. */
334 /* We could postpone this until after returning, if we chose. */
335 (*cinfo->err->output_message) (cinfo);
337 /* Return control to the setjmp point */
338 longjmp(myerr->setjmp_buffer, 1);
342 transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
343 JSAMPROW inrow = *in;
345 /* extract and convert to real CMYK */
346 /* horribly enough this is correct given cmyk values are inverted */
351 out->rgba.r = (c * k) / MAXJSAMPLE;
352 out->rgba.g = (m * k) / MAXJSAMPLE;
353 out->rgba.b = (y * k) / MAXJSAMPLE;
359 transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
360 JSAMPROW inrow = *in;
362 out->rgba.r = *inrow++;
363 out->rgba.g = *inrow++;
364 out->rgba.b = *inrow++;
370 transfer_gray(i_color *out, JSAMPARRAY in, int width) {
371 JSAMPROW inrow = *in;
373 out->gray.gray_color = *inrow++;
378 typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
381 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
386 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
387 i_img * volatile im = NULL;
389 i_color * volatile line_buffer = NULL;
390 struct jpeg_decompress_struct cinfo;
391 struct my_error_mgr jerr;
392 JSAMPARRAY buffer; /* Output row buffer */
393 int row_stride; /* physical row width in output buffer */
394 jpeg_saved_marker_ptr markerp;
395 transfer_function_t transfer_f;
397 volatile int src_set = 0;
399 mm_log((1,"i_readjpeg_wiol(data %p, length %d,iptc_itext %p)\n", data, length, iptc_itext));
403 iptc_text = iptc_itext;
404 cinfo.err = jpeg_std_error(&jerr.pub);
405 jerr.pub.error_exit = my_error_exit;
406 jerr.pub.output_message = my_output_message;
408 /* Set error handler */
409 if (setjmp(jerr.setjmp_buffer)) {
411 wiol_term_source(&cinfo);
412 jpeg_destroy_decompress(&cinfo);
422 jpeg_create_decompress(&cinfo);
423 jpeg_set_marker_processor(&cinfo, JPEG_APP13, APP13_handler);
424 jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
425 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
426 jpeg_wiol_src(&cinfo, data, length);
429 (void) jpeg_read_header(&cinfo, TRUE);
430 (void) jpeg_start_decompress(&cinfo);
432 channels = cinfo.output_components;
433 switch (cinfo.out_color_space) {
435 if (cinfo.output_components != 1) {
436 mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components));
437 i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components);
438 wiol_term_source(&cinfo);
439 jpeg_destroy_decompress(&cinfo);
442 transfer_f = transfer_gray;
446 transfer_f = transfer_rgb;
447 if (cinfo.output_components != 3) {
448 mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components));
449 i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components);
450 wiol_term_source(&cinfo);
451 jpeg_destroy_decompress(&cinfo);
457 if (cinfo.output_components == 4) {
458 /* we treat the CMYK values as inverted, because that's what that
459 buggy photoshop does, and everyone has to follow the gorilla.
461 Is there any app that still produces correct CMYK JPEGs?
463 transfer_f = transfer_cmyk_inverted;
467 mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components));
468 i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components);
469 wiol_term_source(&cinfo);
470 jpeg_destroy_decompress(&cinfo);
476 mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space));
477 i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space);
478 wiol_term_source(&cinfo);
479 jpeg_destroy_decompress(&cinfo);
483 if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
484 channels, sizeof(i_sample_t))) {
485 mm_log((1, "i_readjpeg: image size exceeds limits\n"));
486 wiol_term_source(&cinfo);
487 jpeg_destroy_decompress(&cinfo);
491 im = i_img_8_new(cinfo.output_width, cinfo.output_height, channels);
493 wiol_term_source(&cinfo);
494 jpeg_destroy_decompress(&cinfo);
497 row_stride = cinfo.output_width * cinfo.output_components;
498 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
499 line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
500 while (cinfo.output_scanline < cinfo.output_height) {
501 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
502 transfer_f(line_buffer, buffer, cinfo.output_width);
503 i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
508 /* check for APP1 marker and save */
509 markerp = cinfo.marker_list;
510 while (markerp != NULL) {
511 if (markerp->marker == JPEG_COM) {
512 i_tags_set(&im->tags, "jpeg_comment", (const char *)markerp->data,
513 markerp->data_length);
515 else if (markerp->marker == JPEG_APP1 && !seen_exif) {
516 seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
519 markerp = markerp->next;
522 i_tags_setn(&im->tags, "jpeg_out_color_space", cinfo.out_color_space);
523 i_tags_setn(&im->tags, "jpeg_color_space", cinfo.jpeg_color_space);
525 if (cinfo.saw_JFIF_marker) {
526 double xres = cinfo.X_density;
527 double yres = cinfo.Y_density;
529 i_tags_setn(&im->tags, "jpeg_density_unit", cinfo.density_unit);
530 switch (cinfo.density_unit) {
531 case 0: /* values are just the aspect ratio */
532 i_tags_setn(&im->tags, "i_aspect_only", 1);
533 i_tags_set(&im->tags, "jpeg_density_unit_name", "none", -1);
536 case 1: /* per inch */
537 i_tags_set(&im->tags, "jpeg_density_unit_name", "inch", -1);
541 i_tags_set(&im->tags, "jpeg_density_unit_name", "centimeter", -1);
546 i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
547 i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
550 /* I originally used jpeg_has_multiple_scans() here, but that can
551 * return true for non-progressive files too. The progressive_mode
552 * member is available at least as far back as 6b and does the right
555 i_tags_setn(&im->tags, "jpeg_progressive",
556 cinfo.progressive_mode ? 1 : 0);
558 (void) jpeg_finish_decompress(&cinfo);
559 jpeg_destroy_decompress(&cinfo);
562 i_tags_set(&im->tags, "i_format", "jpeg", 4);
564 mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
569 =item i_writejpeg_wiol(im, ig, qfactor)
575 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
576 JSAMPLE *image_buffer;
578 int got_xres, got_yres, aspect_only, resunit;
581 int want_channels = im->channels;
584 struct jpeg_compress_struct cinfo;
585 struct my_error_mgr jerr;
587 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
588 int row_stride; /* physical row width in image buffer */
589 unsigned char * data = NULL;
590 i_color *line_buf = NULL;
592 mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
596 if (im->xsize > JPEG_DIM_MAX || im->ysize > JPEG_DIM_MAX) {
597 i_push_error(0, "image too large for JPEG");
601 if (!(im->channels==1 || im->channels==3)) {
602 want_channels = im->channels - 1;
606 cinfo.err = jpeg_std_error(&jerr.pub);
607 jerr.pub.error_exit = my_error_exit;
608 jerr.pub.output_message = my_output_message;
610 jpeg_create_compress(&cinfo);
612 if (setjmp(jerr.setjmp_buffer)) {
613 jpeg_destroy_compress(&cinfo);
621 jpeg_wiol_dest(&cinfo, ig);
623 cinfo.image_width = im -> xsize; /* image width and height, in pixels */
624 cinfo.image_height = im -> ysize;
626 if (want_channels==3) {
627 cinfo.input_components = 3; /* # of color components per pixel */
628 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
631 if (want_channels==1) {
632 cinfo.input_components = 1; /* # of color components per pixel */
633 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */
636 jpeg_set_defaults(&cinfo);
637 jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
639 if (!i_tags_get_int(&im->tags, "jpeg_progressive", 0, &progressive))
642 jpeg_simple_progression(&cinfo);
645 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
646 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
647 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
649 if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
650 resunit = 1; /* per inch */
651 if (resunit < 0 || resunit > 2) /* default to inch if invalid */
653 if (got_xres || got_yres) {
659 resunit = 0; /* standard tags override format tags */
661 /* convert to per cm */
665 cinfo.density_unit = resunit;
666 cinfo.X_density = (int)(xres + 0.5);
667 cinfo.Y_density = (int)(yres + 0.5);
670 jpeg_start_compress(&cinfo, TRUE);
672 if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
673 jpeg_write_marker(&cinfo, JPEG_COM,
674 (const JOCTET *)im->tags.tags[comment_entry].data,
675 im->tags.tags[comment_entry].size);
678 row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
680 if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
681 && im->channels == want_channels) {
682 image_buffer=im->idata;
684 while (cinfo.next_scanline < cinfo.image_height) {
685 /* jpeg_write_scanlines expects an array of pointers to scanlines.
686 * Here the array is only one element long, but you could pass
687 * more than one scanline at a time if that's more convenient.
689 row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
690 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
696 i_get_file_background(im, &bg);
697 data = mymalloc(im->xsize * im->channels);
699 while (cinfo.next_scanline < cinfo.image_height) {
700 /* jpeg_write_scanlines expects an array of pointers to scanlines.
701 * Here the array is only one element long, but you could pass
702 * more than one scanline at a time if that's more convenient.
704 i_gsamp_bg(im, 0, im->xsize, cinfo.next_scanline, data,
706 row_pointer[0] = data;
707 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
712 jpeg_destroy_compress(&cinfo);
713 i_push_error(0, "out of memory");
714 return 0; /* out of memory? */
718 /* Step 6: Finish compression */
720 jpeg_finish_compress(&cinfo);
722 jpeg_destroy_compress(&cinfo);
735 Arnar M. Hrafnkelsson, addi@umich.edu