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 static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
45 /* Bad design right here */
48 static char **iptc_text=NULL;
51 /* Source and Destination managers */
55 struct jpeg_source_mgr pub; /* public fields */
57 JOCTET *buffer; /* start of buffer */
58 int length; /* Do I need this? */
59 boolean start_of_file; /* have we gotten any data yet? */
63 struct jpeg_destination_mgr pub; /* public fields */
65 JOCTET *buffer; /* start of buffer */
66 boolean start_of_file; /* have we gotten any data yet? */
67 } wiol_destination_mgr;
69 typedef wiol_source_mgr *wiol_src_ptr;
70 typedef wiol_destination_mgr *wiol_dest_ptr;
74 * Methods for io manager objects
76 * Methods for source managers:
78 * init_source (j_decompress_ptr cinfo);
79 * skip_input_data (j_decompress_ptr cinfo, long num_bytes);
80 * fill_input_buffer (j_decompress_ptr cinfo);
81 * term_source (j_decompress_ptr cinfo);
87 wiol_init_source (j_decompress_ptr cinfo) {
88 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
90 /* We reset the empty-input-file flag for each image, but we don't clear
91 * the input buffer. This is correct behavior for reading a series of
92 * images from one source.
94 src->start_of_file = TRUE;
100 wiol_fill_input_buffer(j_decompress_ptr cinfo) {
101 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
102 ssize_t nbytes; /* We assume that reads are "small" */
104 mm_log((1,"wiol_fill_input_buffer(cinfo 0x%p)\n", cinfo));
106 nbytes = src->data->readcb(src->data, src->buffer, JPGS);
108 if (nbytes <= 0) { /* Insert a fake EOI marker */
109 src->pub.next_input_byte = fake_eoi;
110 src->pub.bytes_in_buffer = 2;
112 src->pub.next_input_byte = src->buffer;
113 src->pub.bytes_in_buffer = nbytes;
115 src->start_of_file = FALSE;
121 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
122 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
124 /* Just a dumb implementation for now. Could use fseek() except
125 * it doesn't work on pipes. Not clear that being smart is worth
126 * any trouble anyway --- large skips are infrequent.
130 while (num_bytes > (long) src->pub.bytes_in_buffer) {
131 num_bytes -= (long) src->pub.bytes_in_buffer;
132 (void) wiol_fill_input_buffer(cinfo);
133 /* note we assume that fill_input_buffer will never return FALSE,
134 * so suspension need not be handled.
137 src->pub.next_input_byte += (size_t) num_bytes;
138 src->pub.bytes_in_buffer -= (size_t) num_bytes;
143 wiol_term_source (j_decompress_ptr cinfo) {
144 /* no work necessary here */
146 if (cinfo->src != NULL) {
147 src = (wiol_src_ptr) cinfo->src;
153 /* Source manager Constructor */
156 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
159 if (cinfo->src == NULL) { /* first time for this JPEG object? */
160 cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
161 ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(wiol_source_mgr));
162 src = (wiol_src_ptr) cinfo->src;
165 /* put the request method call in here later */
166 io_glue_commit_types(ig);
168 src = (wiol_src_ptr) cinfo->src;
170 src->buffer = mymalloc( JPGS );
171 src->length = length;
173 src->pub.init_source = wiol_init_source;
174 src->pub.fill_input_buffer = wiol_fill_input_buffer;
175 src->pub.skip_input_data = wiol_skip_input_data;
176 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
177 src->pub.term_source = wiol_term_source;
178 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
179 src->pub.next_input_byte = NULL; /* until buffer loaded */
186 * Methods for destination managers:
188 * init_destination (j_compress_ptr cinfo);
189 * empty_output_buffer (j_compress_ptr cinfo);
190 * term_destination (j_compress_ptr cinfo);
195 wiol_init_destination (j_compress_ptr cinfo) {
196 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
198 /* We reset the empty-input-file flag for each image, but we don't clear
199 * the input buffer. This is correct behavior for reading a series of
200 * images from one source.
202 dest->start_of_file = TRUE;
206 wiol_empty_output_buffer(j_compress_ptr cinfo) {
207 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
210 Previously this code was checking free_in_buffer to see how much
211 needed to be written. This does not follow the documentation:
213 "In typical applications, it should write out the
214 *entire* buffer (use the saved start address and buffer length;
215 ignore the current state of next_output_byte and free_in_buffer)."
217 ssize_t nbytes = JPGS - dest->pub.free_in_buffer;
220 mm_log((1,"wiol_empty_output_buffer(cinfo 0x%p)\n", cinfo));
221 rc = dest->data->writecb(dest->data, dest->buffer, JPGS);
223 if (rc != JPGS) { /* XXX: Should raise some jpeg error */
224 myfree(dest->buffer);
225 mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, rc));
226 ERREXIT(cinfo, JERR_FILE_WRITE);
228 dest->pub.free_in_buffer = JPGS;
229 dest->pub.next_output_byte = dest->buffer;
234 wiol_term_destination (j_compress_ptr cinfo) {
235 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
236 size_t nbytes = JPGS - dest->pub.free_in_buffer;
237 /* yes, this needs to flush the buffer */
238 /* needs error handling */
240 if (dest->data->writecb(dest->data, dest->buffer, nbytes) != nbytes) {
241 myfree(dest->buffer);
242 ERREXIT(cinfo, JERR_FILE_WRITE);
245 if (dest != NULL) myfree(dest->buffer);
249 /* Destination manager Constructor */
252 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
255 if (cinfo->dest == NULL) { /* first time for this JPEG object? */
257 (struct jpeg_destination_mgr *)
258 (*cinfo->mem->alloc_small)
259 ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
262 dest = (wiol_dest_ptr) cinfo->dest;
264 dest->buffer = mymalloc( JPGS );
266 dest->pub.init_destination = wiol_init_destination;
267 dest->pub.empty_output_buffer = wiol_empty_output_buffer;
268 dest->pub.term_destination = wiol_term_destination;
269 dest->pub.free_in_buffer = JPGS;
270 dest->pub.next_output_byte = dest->buffer;
274 jpeg_getc (j_decompress_ptr cinfo)
277 struct jpeg_source_mgr * datasrc = cinfo->src;
279 if (datasrc->bytes_in_buffer == 0) {
280 if (! (*datasrc->fill_input_buffer) (cinfo))
281 { fprintf(stderr,"Jpeglib: cant suspend.\n"); exit(3); }
282 /* ERREXIT(cinfo, JERR_CANT_SUSPEND);*/
284 datasrc->bytes_in_buffer--;
285 return GETJOCTET(*datasrc->next_input_byte++);
289 APP13_handler (j_decompress_ptr cinfo) {
293 length = jpeg_getc(cinfo) << 8;
294 length += jpeg_getc(cinfo);
295 length -= 2; /* discount the length word itself */
299 if ( ((*iptc_text)=mymalloc(length)) == NULL ) return FALSE;
300 while (--length >= 0) (*iptc_text)[cnt++] = jpeg_getc(cinfo);
306 my_output_message (j_common_ptr cinfo) {
307 char buffer[JMSG_LENGTH_MAX];
309 /* Create the message */
310 (*cinfo->err->format_message) (cinfo, buffer);
312 i_push_error(0, buffer);
314 /* Send it to stderr, adding a newline */
315 mm_log((1, "%s\n", buffer));
318 struct my_error_mgr {
319 struct jpeg_error_mgr pub; /* "public" fields */
320 jmp_buf setjmp_buffer; /* for return to caller */
323 typedef struct my_error_mgr * my_error_ptr;
325 /* Here's the routine that will replace the standard error_exit method */
328 my_error_exit (j_common_ptr cinfo) {
329 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
330 my_error_ptr myerr = (my_error_ptr) cinfo->err;
332 /* Always display the message. */
333 /* We could postpone this until after returning, if we chose. */
334 (*cinfo->err->output_message) (cinfo);
336 /* Return control to the setjmp point */
337 longjmp(myerr->setjmp_buffer, 1);
341 transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
342 JSAMPROW inrow = *in;
344 /* extract and convert to real CMYK */
345 /* horribly enough this is correct given cmyk values are inverted */
350 out->rgba.r = (c * k) / MAXJSAMPLE;
351 out->rgba.g = (m * k) / MAXJSAMPLE;
352 out->rgba.b = (y * k) / MAXJSAMPLE;
358 transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
359 JSAMPROW inrow = *in;
361 out->rgba.r = *inrow++;
362 out->rgba.g = *inrow++;
363 out->rgba.b = *inrow++;
369 transfer_gray(i_color *out, JSAMPARRAY in, int width) {
370 JSAMPROW inrow = *in;
372 out->gray.gray_color = *inrow++;
377 typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
380 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
385 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
386 i_img * volatile im = NULL;
390 i_color *line_buffer = NULL;
391 struct jpeg_decompress_struct cinfo;
392 struct my_error_mgr jerr;
393 JSAMPARRAY buffer; /* Output row buffer */
394 int row_stride; /* physical row width in output buffer */
395 jpeg_saved_marker_ptr markerp;
396 transfer_function_t transfer_f;
398 volatile int src_set = 0;
400 mm_log((1,"i_readjpeg_wiol(data 0x%p, length %d,iptc_itext 0x%p)\n", data, length, iptc_itext));
404 iptc_text = iptc_itext;
405 cinfo.err = jpeg_std_error(&jerr.pub);
406 jerr.pub.error_exit = my_error_exit;
407 jerr.pub.output_message = my_output_message;
409 /* Set error handler */
410 if (setjmp(jerr.setjmp_buffer)) {
412 wiol_term_source(&cinfo);
413 jpeg_destroy_decompress(&cinfo);
423 jpeg_create_decompress(&cinfo);
424 jpeg_set_marker_processor(&cinfo, JPEG_APP13, APP13_handler);
425 jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
426 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
427 jpeg_wiol_src(&cinfo, data, length);
430 (void) jpeg_read_header(&cinfo, TRUE);
431 (void) jpeg_start_decompress(&cinfo);
433 channels = cinfo.output_components;
434 switch (cinfo.out_color_space) {
436 if (cinfo.output_components != 1) {
437 mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components));
438 i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components);
439 wiol_term_source(&cinfo);
440 jpeg_destroy_decompress(&cinfo);
443 transfer_f = transfer_gray;
447 transfer_f = transfer_rgb;
448 if (cinfo.output_components != 3) {
449 mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components));
450 i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components);
451 wiol_term_source(&cinfo);
452 jpeg_destroy_decompress(&cinfo);
458 if (cinfo.output_components == 4) {
459 /* we treat the CMYK values as inverted, because that's what that
460 buggy photoshop does, and everyone has to follow the gorilla.
462 Is there any app that still produces correct CMYK JPEGs?
464 transfer_f = transfer_cmyk_inverted;
468 mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components));
469 i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components);
470 wiol_term_source(&cinfo);
471 jpeg_destroy_decompress(&cinfo);
477 mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space));
478 i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space);
479 wiol_term_source(&cinfo);
480 jpeg_destroy_decompress(&cinfo);
484 if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
485 channels, sizeof(i_sample_t))) {
486 mm_log((1, "i_readjpeg: image size exceeds limits\n"));
487 wiol_term_source(&cinfo);
488 jpeg_destroy_decompress(&cinfo);
492 im=i_img_empty_ch(NULL, cinfo.output_width, cinfo.output_height, channels);
494 wiol_term_source(&cinfo);
495 jpeg_destroy_decompress(&cinfo);
498 row_stride = cinfo.output_width * cinfo.output_components;
499 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
500 line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
501 while (cinfo.output_scanline < cinfo.output_height) {
502 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
503 transfer_f(line_buffer, buffer, cinfo.output_width);
504 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_add(&im->tags, "jpeg_comment", 0, (const char *)markerp->data,
513 markerp->data_length, 0);
516 else if (markerp->marker == JPEG_APP1 && !seen_exif) {
517 seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
521 markerp = markerp->next;
524 i_tags_addn(&im->tags, "jpeg_out_color_space", 0, cinfo.out_color_space);
525 i_tags_addn(&im->tags, "jpeg_color_space", 0, cinfo.jpeg_color_space);
527 if (cinfo.saw_JFIF_marker) {
528 double xres = cinfo.X_density;
529 double yres = cinfo.Y_density;
531 i_tags_addn(&im->tags, "jpeg_density_unit", 0, cinfo.density_unit);
532 switch (cinfo.density_unit) {
533 case 0: /* values are just the aspect ratio */
534 i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
535 i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "none", -1, 0);
538 case 1: /* per inch */
539 i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "inch", -1, 0);
543 i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "centimeter", -1, 0);
548 i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
549 i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
552 (void) jpeg_finish_decompress(&cinfo);
553 jpeg_destroy_decompress(&cinfo);
556 i_tags_add(&im->tags, "i_format", 0, "jpeg", 4, 0);
558 mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
563 =item i_writejpeg_wiol(im, ig, qfactor)
569 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
570 JSAMPLE *image_buffer;
572 int got_xres, got_yres, aspect_only, resunit;
575 int want_channels = im->channels;
577 struct jpeg_compress_struct cinfo;
578 struct my_error_mgr jerr;
580 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
581 int row_stride; /* physical row width in image buffer */
582 unsigned char * data = NULL;
583 i_color *line_buf = NULL;
585 mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
588 io_glue_commit_types(ig);
590 if (!(im->channels==1 || im->channels==3)) {
591 want_channels = im->channels - 1;
595 cinfo.err = jpeg_std_error(&jerr.pub);
596 jerr.pub.error_exit = my_error_exit;
597 jerr.pub.output_message = my_output_message;
599 jpeg_create_compress(&cinfo);
601 if (setjmp(jerr.setjmp_buffer)) {
602 jpeg_destroy_compress(&cinfo);
610 jpeg_wiol_dest(&cinfo, ig);
612 cinfo.image_width = im -> xsize; /* image width and height, in pixels */
613 cinfo.image_height = im -> ysize;
615 if (want_channels==3) {
616 cinfo.input_components = 3; /* # of color components per pixel */
617 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
620 if (want_channels==1) {
621 cinfo.input_components = 1; /* # of color components per pixel */
622 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */
625 jpeg_set_defaults(&cinfo);
626 jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
628 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
629 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
630 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
632 if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
633 resunit = 1; /* per inch */
634 if (resunit < 0 || resunit > 2) /* default to inch if invalid */
636 if (got_xres || got_yres) {
642 resunit = 0; /* standard tags override format tags */
644 /* convert to per cm */
648 cinfo.density_unit = resunit;
649 cinfo.X_density = (int)(xres + 0.5);
650 cinfo.Y_density = (int)(yres + 0.5);
653 jpeg_start_compress(&cinfo, TRUE);
655 if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
656 jpeg_write_marker(&cinfo, JPEG_COM,
657 (const JOCTET *)im->tags.tags[comment_entry].data,
658 im->tags.tags[comment_entry].size);
661 row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
663 if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
664 && im->channels == want_channels) {
665 image_buffer=im->idata;
667 while (cinfo.next_scanline < cinfo.image_height) {
668 /* jpeg_write_scanlines expects an array of pointers to scanlines.
669 * Here the array is only one element long, but you could pass
670 * more than one scanline at a time if that's more convenient.
672 row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
673 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
676 else if (im->channels == want_channels) {
677 data = mymalloc(im->xsize * im->channels);
679 while (cinfo.next_scanline < cinfo.image_height) {
680 /* jpeg_write_scanlines expects an array of pointers to scanlines.
681 * Here the array is only one element long, but you could pass
682 * more than one scanline at a time if that's more convenient.
684 i_gsamp(im, 0, im->xsize, cinfo.next_scanline, data,
686 row_pointer[0] = data;
687 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
692 jpeg_destroy_compress(&cinfo);
693 i_push_error(0, "out of memory");
694 return 0; /* out of memory? */
701 i_color const *linep;
702 unsigned char * datap;
704 line_buf = mymalloc(sizeof(i_color) * im->xsize);
706 i_get_file_background(im, &bg);
708 data = mymalloc(im->xsize * want_channels);
709 while (cinfo.next_scanline < cinfo.image_height) {
710 i_glin(im, 0, im->xsize, cinfo.next_scanline, line_buf);
711 i_adapt_colors_bg(want_channels, im->channels, line_buf, im->xsize, &bg);
714 for (x = 0; x < im->xsize; ++x) {
715 for (ch = 0; ch < want_channels; ++ch) {
716 *datap++ = linep->channel[ch];
720 row_pointer[0] = data;
721 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
727 /* Step 6: Finish compression */
729 jpeg_finish_compress(&cinfo);
731 jpeg_destroy_compress(&cinfo);
743 Arnar M. Hrafnkelsson, addi@umich.edu