X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/92bda6321b472bb18726d950da8833b950abf4ee..bcff4dd91440c0b7fb0527ea59e6a8ecb85852a9:/jpeg.c diff --git a/jpeg.c b/jpeg.c index 42535d5a..90573b7a 100644 --- a/jpeg.c +++ b/jpeg.c @@ -101,7 +101,7 @@ wiol_fill_input_buffer(j_decompress_ptr cinfo) { wiol_src_ptr src = (wiol_src_ptr) cinfo->src; ssize_t nbytes; /* We assume that reads are "small" */ - mm_log((1,"wiol_fill_input_buffer(cinfo 0x%p)\n")); + mm_log((1,"wiol_fill_input_buffer(cinfo 0x%p)\n", cinfo)); nbytes = src->data->readcb(src->data, src->buffer, JPGS); @@ -217,7 +217,7 @@ wiol_empty_output_buffer(j_compress_ptr cinfo) { ssize_t nbytes = JPGS - dest->pub.free_in_buffer; */ - mm_log((1,"wiol_empty_output_buffer(cinfo 0x%p)\n")); + mm_log((1,"wiol_empty_output_buffer(cinfo 0x%p)\n", cinfo)); rc = dest->data->writecb(dest->data, dest->buffer, JPGS); if (rc != JPGS) { /* XXX: Should raise some jpeg error */ @@ -337,6 +337,45 @@ my_error_exit (j_common_ptr cinfo) { longjmp(myerr->setjmp_buffer, 1); } +static void +transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) { + JSAMPROW inrow = *in; + while (width--) { + /* extract and convert to real CMYK */ + /* horribly enough this is correct given cmyk values are inverted */ + int c = *inrow++; + int m = *inrow++; + int y = *inrow++; + int k = *inrow++; + out->rgba.r = (c * k) / MAXJSAMPLE; + out->rgba.g = (m * k) / MAXJSAMPLE; + out->rgba.b = (y * k) / MAXJSAMPLE; + ++out; + } +} + +static void +transfer_rgb(i_color *out, JSAMPARRAY in, int width) { + JSAMPROW inrow = *in; + while (width--) { + out->rgba.r = *inrow++; + out->rgba.g = *inrow++; + out->rgba.b = *inrow++; + ++out; + } +} + +static void +transfer_gray(i_color *out, JSAMPARRAY in, int width) { + JSAMPROW inrow = *in; + while (width--) { + out->gray.gray_color = *inrow++; + ++out; + } +} + +typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width); + /* =item i_readjpeg_wiol(data, length, iptc_itext, itlength) @@ -348,14 +387,16 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) { #ifdef IMEXIF_ENABLE int seen_exif = 0; #endif - + i_color *line_buffer = NULL; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; JSAMPARRAY buffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ jpeg_saved_marker_ptr markerp; + transfer_function_t transfer_f; + int channels; - mm_log((1,"i_readjpeg_wiol(data 0x%p, length %d,iptc_itext 0x%p)\n", data, iptc_itext)); + mm_log((1,"i_readjpeg_wiol(data 0x%p, length %d,iptc_itext 0x%p)\n", data, length, iptc_itext)); i_clear_error(); @@ -369,6 +410,8 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) { jpeg_destroy_decompress(&cinfo); *iptc_itext=NULL; *itlength=0; + if (line_buffer) + myfree(line_buffer); return NULL; } @@ -380,30 +423,87 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) { (void) jpeg_read_header(&cinfo, TRUE); (void) jpeg_start_decompress(&cinfo); + + channels = cinfo.output_components; + switch (cinfo.out_color_space) { + case JCS_GRAYSCALE: + if (cinfo.output_components != 1) { + mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components)); + i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components); + wiol_term_source(&cinfo); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + transfer_f = transfer_gray; + break; + + case JCS_RGB: + transfer_f = transfer_rgb; + if (cinfo.output_components != 3) { + mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components)); + i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components); + wiol_term_source(&cinfo); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + break; + + case JCS_CMYK: + if (cinfo.output_components == 4) { + /* we treat the CMYK values as inverted, because that's what that + buggy photoshop does, and everyone has to follow the gorilla. + + Is there any app that still produces correct CMYK JPEGs? + */ + transfer_f = transfer_cmyk_inverted; + channels = 3; + } + else { + mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components)); + i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components); + wiol_term_source(&cinfo); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + break; + + default: + mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space)); + i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space); + wiol_term_source(&cinfo); + jpeg_destroy_decompress(&cinfo); + return NULL; + } + if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height, - cinfo.output_components, sizeof(i_sample_t))) { + channels, sizeof(i_sample_t))) { mm_log((1, "i_readjpeg: image size exceeds limits\n")); - + wiol_term_source(&cinfo); jpeg_destroy_decompress(&cinfo); return NULL; } - im=i_img_empty_ch(NULL,cinfo.output_width,cinfo.output_height,cinfo.output_components); + + im=i_img_empty_ch(NULL, cinfo.output_width, cinfo.output_height, channels); if (!im) { + wiol_term_source(&cinfo); jpeg_destroy_decompress(&cinfo); return NULL; } row_stride = cinfo.output_width * cinfo.output_components; buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width); while (cinfo.output_scanline < cinfo.output_height) { (void) jpeg_read_scanlines(&cinfo, buffer, 1); - memcpy(im->idata+im->channels*im->xsize*(cinfo.output_scanline-1),buffer[0],row_stride); + transfer_f(line_buffer, buffer, cinfo.output_width); + i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer); } + myfree(line_buffer); /* check for APP1 marker and save */ markerp = cinfo.marker_list; while (markerp != NULL) { if (markerp->marker == JPEG_COM) { - i_tags_add(&im->tags, "jpeg_comment", 0, markerp->data, + i_tags_add(&im->tags, "jpeg_comment", 0, (const char *)markerp->data, markerp->data_length, 0); } #ifdef IMEXIF_ENABLE @@ -415,6 +515,9 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) { markerp = markerp->next; } + i_tags_addn(&im->tags, "jpeg_out_color_space", 0, cinfo.out_color_space); + i_tags_addn(&im->tags, "jpeg_color_space", 0, cinfo.jpeg_color_space); + if (cinfo.saw_JFIF_marker) { double xres = cinfo.X_density; double yres = cinfo.Y_density; @@ -474,6 +577,7 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) { mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor)); i_clear_error(); + io_glue_commit_types(ig); if (!(im->channels==1 || im->channels==3)) { i_push_error(0, "only 1 or 3 channels images can be saved as JPEG"); @@ -494,7 +598,6 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) { return 0; } - io_glue_commit_types(ig); jpeg_wiol_dest(&cinfo, ig); cinfo.image_width = im -> xsize; /* image width and height, in pixels */ @@ -541,7 +644,8 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) { jpeg_start_compress(&cinfo, TRUE); if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) { - jpeg_write_marker(&cinfo, JPEG_COM, im->tags.tags[comment_entry].data, + jpeg_write_marker(&cinfo, JPEG_COM, + (const JOCTET *)im->tags.tags[comment_entry].data, im->tags.tags[comment_entry].size); } @@ -572,6 +676,7 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) { row_pointer[0] = data; (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); } + myfree(data); } else { jpeg_destroy_compress(&cinfo);