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
37 #define JPEG_APP13 0xED /* APP13 marker code */
38 #define JPEG_APP1 (JPEG_APP0 + 1)
41 static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
43 /* Bad design right here */
46 static char **iptc_text=NULL;
49 /* 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;
72 * Methods for io manager objects
74 * Methods for source managers:
76 * init_source (j_decompress_ptr cinfo);
77 * skip_input_data (j_decompress_ptr cinfo, long num_bytes);
78 * fill_input_buffer (j_decompress_ptr cinfo);
79 * term_source (j_decompress_ptr cinfo);
85 wiol_init_source (j_decompress_ptr cinfo) {
86 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
88 /* We reset the empty-input-file flag for each image, but we don't clear
89 * the input buffer. This is correct behavior for reading a series of
90 * images from one source.
92 src->start_of_file = TRUE;
98 wiol_fill_input_buffer(j_decompress_ptr cinfo) {
99 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
100 ssize_t nbytes; /* We assume that reads are "small" */
102 mm_log((1,"wiol_fill_input_buffer(cinfo 0x%p)\n", cinfo));
104 nbytes = src->data->readcb(src->data, src->buffer, JPGS);
106 if (nbytes <= 0) { /* Insert a fake EOI marker */
107 src->pub.next_input_byte = fake_eoi;
108 src->pub.bytes_in_buffer = 2;
110 src->pub.next_input_byte = src->buffer;
111 src->pub.bytes_in_buffer = nbytes;
113 src->start_of_file = FALSE;
119 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
120 wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
122 /* Just a dumb implementation for now. Could use fseek() except
123 * it doesn't work on pipes. Not clear that being smart is worth
124 * any trouble anyway --- large skips are infrequent.
128 while (num_bytes > (long) src->pub.bytes_in_buffer) {
129 num_bytes -= (long) src->pub.bytes_in_buffer;
130 (void) wiol_fill_input_buffer(cinfo);
131 /* note we assume that fill_input_buffer will never return FALSE,
132 * so suspension need not be handled.
135 src->pub.next_input_byte += (size_t) num_bytes;
136 src->pub.bytes_in_buffer -= (size_t) num_bytes;
141 wiol_term_source (j_decompress_ptr cinfo) {
142 /* no work necessary here */
144 if (cinfo->src != NULL) {
145 src = (wiol_src_ptr) cinfo->src;
151 /* Source manager Constructor */
154 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
157 if (cinfo->src == NULL) { /* first time for this JPEG object? */
158 cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
159 ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(wiol_source_mgr));
160 src = (wiol_src_ptr) cinfo->src;
163 /* put the request method call in here later */
165 src = (wiol_src_ptr) cinfo->src;
167 src->buffer = mymalloc( JPGS );
168 src->length = length;
170 src->pub.init_source = wiol_init_source;
171 src->pub.fill_input_buffer = wiol_fill_input_buffer;
172 src->pub.skip_input_data = wiol_skip_input_data;
173 src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
174 src->pub.term_source = wiol_term_source;
175 src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
176 src->pub.next_input_byte = NULL; /* until buffer loaded */
183 * Methods for destination managers:
185 * init_destination (j_compress_ptr cinfo);
186 * empty_output_buffer (j_compress_ptr cinfo);
187 * term_destination (j_compress_ptr cinfo);
192 wiol_init_destination (j_compress_ptr cinfo) {
193 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
195 /* We reset the empty-input-file flag for each image, but we don't clear
196 * the input buffer. This is correct behavior for reading a series of
197 * images from one source.
199 dest->start_of_file = TRUE;
203 wiol_empty_output_buffer(j_compress_ptr cinfo) {
204 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
207 Previously this code was checking free_in_buffer to see how much
208 needed to be written. This does not follow the documentation:
210 "In typical applications, it should write out the
211 *entire* buffer (use the saved start address and buffer length;
212 ignore the current state of next_output_byte and free_in_buffer)."
214 ssize_t nbytes = JPGS - dest->pub.free_in_buffer;
217 mm_log((1,"wiol_empty_output_buffer(cinfo 0x%p)\n", cinfo));
218 rc = dest->data->writecb(dest->data, dest->buffer, JPGS);
220 if (rc != JPGS) { /* XXX: Should raise some jpeg error */
221 myfree(dest->buffer);
222 mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, rc));
223 ERREXIT(cinfo, JERR_FILE_WRITE);
225 dest->pub.free_in_buffer = JPGS;
226 dest->pub.next_output_byte = dest->buffer;
231 wiol_term_destination (j_compress_ptr cinfo) {
232 wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
233 size_t nbytes = JPGS - dest->pub.free_in_buffer;
234 /* yes, this needs to flush the buffer */
235 /* needs error handling */
237 if (dest->data->writecb(dest->data, dest->buffer, nbytes) != nbytes) {
238 myfree(dest->buffer);
239 ERREXIT(cinfo, JERR_FILE_WRITE);
242 if (dest != NULL) myfree(dest->buffer);
246 /* Destination manager Constructor */
249 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
252 if (cinfo->dest == NULL) { /* first time for this JPEG object? */
254 (struct jpeg_destination_mgr *)
255 (*cinfo->mem->alloc_small)
256 ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
259 dest = (wiol_dest_ptr) cinfo->dest;
261 dest->buffer = mymalloc( JPGS );
263 dest->pub.init_destination = wiol_init_destination;
264 dest->pub.empty_output_buffer = wiol_empty_output_buffer;
265 dest->pub.term_destination = wiol_term_destination;
266 dest->pub.free_in_buffer = JPGS;
267 dest->pub.next_output_byte = dest->buffer;
271 jpeg_getc (j_decompress_ptr cinfo)
274 struct jpeg_source_mgr * datasrc = cinfo->src;
276 if (datasrc->bytes_in_buffer == 0) {
277 if (! (*datasrc->fill_input_buffer) (cinfo))
278 { fprintf(stderr,"Jpeglib: cant suspend.\n"); exit(3); }
279 /* ERREXIT(cinfo, JERR_CANT_SUSPEND);*/
281 datasrc->bytes_in_buffer--;
282 return GETJOCTET(*datasrc->next_input_byte++);
286 APP13_handler (j_decompress_ptr cinfo) {
290 length = jpeg_getc(cinfo) << 8;
291 length += jpeg_getc(cinfo);
292 length -= 2; /* discount the length word itself */
296 if ( ((*iptc_text)=mymalloc(length)) == NULL ) return FALSE;
297 while (--length >= 0) (*iptc_text)[cnt++] = jpeg_getc(cinfo);
303 my_output_message (j_common_ptr cinfo) {
304 char buffer[JMSG_LENGTH_MAX];
306 /* Create the message */
307 (*cinfo->err->format_message) (cinfo, buffer);
309 i_push_error(0, buffer);
311 /* Send it to stderr, adding a newline */
312 mm_log((1, "%s\n", buffer));
315 struct my_error_mgr {
316 struct jpeg_error_mgr pub; /* "public" fields */
317 jmp_buf setjmp_buffer; /* for return to caller */
320 typedef struct my_error_mgr * my_error_ptr;
322 /* Here's the routine that will replace the standard error_exit method */
325 my_error_exit (j_common_ptr cinfo) {
326 /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
327 my_error_ptr myerr = (my_error_ptr) cinfo->err;
329 /* Always display the message. */
330 /* We could postpone this until after returning, if we chose. */
331 (*cinfo->err->output_message) (cinfo);
333 /* Return control to the setjmp point */
334 longjmp(myerr->setjmp_buffer, 1);
338 transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
339 JSAMPROW inrow = *in;
341 /* extract and convert to real CMYK */
342 /* horribly enough this is correct given cmyk values are inverted */
347 out->rgba.r = (c * k) / MAXJSAMPLE;
348 out->rgba.g = (m * k) / MAXJSAMPLE;
349 out->rgba.b = (y * k) / MAXJSAMPLE;
355 transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
356 JSAMPROW inrow = *in;
358 out->rgba.r = *inrow++;
359 out->rgba.g = *inrow++;
360 out->rgba.b = *inrow++;
366 transfer_gray(i_color *out, JSAMPARRAY in, int width) {
367 JSAMPROW inrow = *in;
369 out->gray.gray_color = *inrow++;
374 typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
377 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
382 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
383 i_img * volatile im = NULL;
385 i_color * volatile line_buffer = NULL;
386 struct jpeg_decompress_struct cinfo;
387 struct my_error_mgr jerr;
388 JSAMPARRAY buffer; /* Output row buffer */
389 int row_stride; /* physical row width in output buffer */
390 jpeg_saved_marker_ptr markerp;
391 transfer_function_t transfer_f;
393 volatile int src_set = 0;
395 mm_log((1,"i_readjpeg_wiol(data 0x%p, length %d,iptc_itext 0x%p)\n", data, length, iptc_itext));
399 iptc_text = iptc_itext;
400 cinfo.err = jpeg_std_error(&jerr.pub);
401 jerr.pub.error_exit = my_error_exit;
402 jerr.pub.output_message = my_output_message;
404 /* Set error handler */
405 if (setjmp(jerr.setjmp_buffer)) {
407 wiol_term_source(&cinfo);
408 jpeg_destroy_decompress(&cinfo);
418 jpeg_create_decompress(&cinfo);
419 jpeg_set_marker_processor(&cinfo, JPEG_APP13, APP13_handler);
420 jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
421 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
422 jpeg_wiol_src(&cinfo, data, length);
425 (void) jpeg_read_header(&cinfo, TRUE);
426 (void) jpeg_start_decompress(&cinfo);
428 channels = cinfo.output_components;
429 switch (cinfo.out_color_space) {
431 if (cinfo.output_components != 1) {
432 mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components));
433 i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components);
434 wiol_term_source(&cinfo);
435 jpeg_destroy_decompress(&cinfo);
438 transfer_f = transfer_gray;
442 transfer_f = transfer_rgb;
443 if (cinfo.output_components != 3) {
444 mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components));
445 i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components);
446 wiol_term_source(&cinfo);
447 jpeg_destroy_decompress(&cinfo);
453 if (cinfo.output_components == 4) {
454 /* we treat the CMYK values as inverted, because that's what that
455 buggy photoshop does, and everyone has to follow the gorilla.
457 Is there any app that still produces correct CMYK JPEGs?
459 transfer_f = transfer_cmyk_inverted;
463 mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components));
464 i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components);
465 wiol_term_source(&cinfo);
466 jpeg_destroy_decompress(&cinfo);
472 mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space));
473 i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space);
474 wiol_term_source(&cinfo);
475 jpeg_destroy_decompress(&cinfo);
479 if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
480 channels, sizeof(i_sample_t))) {
481 mm_log((1, "i_readjpeg: image size exceeds limits\n"));
482 wiol_term_source(&cinfo);
483 jpeg_destroy_decompress(&cinfo);
487 im = i_img_8_new(cinfo.output_width, cinfo.output_height, channels);
489 wiol_term_source(&cinfo);
490 jpeg_destroy_decompress(&cinfo);
493 row_stride = cinfo.output_width * cinfo.output_components;
494 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
495 line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
496 while (cinfo.output_scanline < cinfo.output_height) {
497 (void) jpeg_read_scanlines(&cinfo, buffer, 1);
498 transfer_f(line_buffer, buffer, cinfo.output_width);
499 i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
504 /* check for APP1 marker and save */
505 markerp = cinfo.marker_list;
506 while (markerp != NULL) {
507 if (markerp->marker == JPEG_COM) {
508 i_tags_set(&im->tags, "jpeg_comment", (const char *)markerp->data,
509 markerp->data_length);
511 else if (markerp->marker == JPEG_APP1 && !seen_exif) {
512 seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
515 markerp = markerp->next;
518 i_tags_setn(&im->tags, "jpeg_out_color_space", cinfo.out_color_space);
519 i_tags_setn(&im->tags, "jpeg_color_space", cinfo.jpeg_color_space);
521 if (cinfo.saw_JFIF_marker) {
522 double xres = cinfo.X_density;
523 double yres = cinfo.Y_density;
525 i_tags_setn(&im->tags, "jpeg_density_unit", cinfo.density_unit);
526 switch (cinfo.density_unit) {
527 case 0: /* values are just the aspect ratio */
528 i_tags_setn(&im->tags, "i_aspect_only", 1);
529 i_tags_set(&im->tags, "jpeg_density_unit_name", "none", -1);
532 case 1: /* per inch */
533 i_tags_set(&im->tags, "jpeg_density_unit_name", "inch", -1);
537 i_tags_set(&im->tags, "jpeg_density_unit_name", "centimeter", -1);
542 i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
543 i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
546 (void) jpeg_finish_decompress(&cinfo);
547 jpeg_destroy_decompress(&cinfo);
550 i_tags_set(&im->tags, "i_format", "jpeg", 4);
552 mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
557 =item i_writejpeg_wiol(im, ig, qfactor)
563 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
564 JSAMPLE *image_buffer;
566 int got_xres, got_yres, aspect_only, resunit;
569 int want_channels = im->channels;
571 struct jpeg_compress_struct cinfo;
572 struct my_error_mgr jerr;
574 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
575 int row_stride; /* physical row width in image buffer */
576 unsigned char * data = NULL;
577 i_color *line_buf = NULL;
579 mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
583 if (!(im->channels==1 || im->channels==3)) {
584 want_channels = im->channels - 1;
588 cinfo.err = jpeg_std_error(&jerr.pub);
589 jerr.pub.error_exit = my_error_exit;
590 jerr.pub.output_message = my_output_message;
592 jpeg_create_compress(&cinfo);
594 if (setjmp(jerr.setjmp_buffer)) {
595 jpeg_destroy_compress(&cinfo);
603 jpeg_wiol_dest(&cinfo, ig);
605 cinfo.image_width = im -> xsize; /* image width and height, in pixels */
606 cinfo.image_height = im -> ysize;
608 if (want_channels==3) {
609 cinfo.input_components = 3; /* # of color components per pixel */
610 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
613 if (want_channels==1) {
614 cinfo.input_components = 1; /* # of color components per pixel */
615 cinfo.in_color_space = JCS_GRAYSCALE; /* colorspace of input image */
618 jpeg_set_defaults(&cinfo);
619 jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
621 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
622 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
623 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
625 if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
626 resunit = 1; /* per inch */
627 if (resunit < 0 || resunit > 2) /* default to inch if invalid */
629 if (got_xres || got_yres) {
635 resunit = 0; /* standard tags override format tags */
637 /* convert to per cm */
641 cinfo.density_unit = resunit;
642 cinfo.X_density = (int)(xres + 0.5);
643 cinfo.Y_density = (int)(yres + 0.5);
646 jpeg_start_compress(&cinfo, TRUE);
648 if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
649 jpeg_write_marker(&cinfo, JPEG_COM,
650 (const JOCTET *)im->tags.tags[comment_entry].data,
651 im->tags.tags[comment_entry].size);
654 row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
656 if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
657 && im->channels == want_channels) {
658 image_buffer=im->idata;
660 while (cinfo.next_scanline < cinfo.image_height) {
661 /* jpeg_write_scanlines expects an array of pointers to scanlines.
662 * Here the array is only one element long, but you could pass
663 * more than one scanline at a time if that's more convenient.
665 row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
666 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
672 i_get_file_background(im, &bg);
673 data = mymalloc(im->xsize * im->channels);
675 while (cinfo.next_scanline < cinfo.image_height) {
676 /* jpeg_write_scanlines expects an array of pointers to scanlines.
677 * Here the array is only one element long, but you could pass
678 * more than one scanline at a time if that's more convenient.
680 i_gsamp_bg(im, 0, im->xsize, cinfo.next_scanline, data,
682 row_pointer[0] = data;
683 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
688 jpeg_destroy_compress(&cinfo);
689 i_push_error(0, "out of memory");
690 return 0; /* out of memory? */
694 /* Step 6: Finish compression */
696 jpeg_finish_compress(&cinfo);
698 jpeg_destroy_compress(&cinfo);
710 Arnar M. Hrafnkelsson, addi@umich.edu