X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/f7450478e1b5716c80039a8a697f5818bdcffbb1..c6a46fc13bacf16b447cb260b3cef372e614a277:/jpeg.c diff --git a/jpeg.c b/jpeg.c index d6c7f03d..1215fa0c 100644 --- a/jpeg.c +++ b/jpeg.c @@ -28,7 +28,7 @@ Reads and writes JPEG images #include #include "iolayer.h" -#include "imagei.h" +#include "imageri.h" #include "jpeglib.h" #include "jerror.h" #include @@ -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,73 @@ 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: + transfer_f = transfer_gray; + break; + + case JCS_RGB: + transfer_f = transfer_rgb; + 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 +501,34 @@ 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; + + i_tags_addn(&im->tags, "jpeg_density_unit", 0, cinfo.density_unit); + switch (cinfo.density_unit) { + case 0: /* values are just the aspect ratio */ + i_tags_addn(&im->tags, "i_aspect_only", 0, 1); + i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "none", -1, 0); + break; + + case 1: /* per inch */ + i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "inch", -1, 0); + break; + + case 2: /* per cm */ + i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "centimeter", -1, 0); + xres *= 2.54; + yres *= 2.54; + break; + } + i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6); + i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6); + } + (void) jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); *itlength=tlength; @@ -435,6 +549,9 @@ undef_int i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) { JSAMPLE *image_buffer; int quality; + int got_xres, got_yres, aspect_only, resunit; + double xres, yres; + int comment_entry; struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; @@ -446,6 +563,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"); @@ -466,7 +584,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 */ @@ -485,8 +602,39 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) { jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */ + got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres); + got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres); + if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only)) + aspect_only = 0; + if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit)) + resunit = 1; /* per inch */ + if (resunit < 0 || resunit > 2) /* default to inch if invalid */ + resunit = 1; + if (got_xres || got_yres) { + if (!got_xres) + xres = yres; + else if (!got_yres) + yres = xres; + if (aspect_only) + resunit = 0; /* standard tags override format tags */ + if (resunit == 2) { + /* convert to per cm */ + xres /= 2.54; + yres /= 2.54; + } + cinfo.density_unit = resunit; + cinfo.X_density = (int)(xres + 0.5); + cinfo.Y_density = (int)(yres + 0.5); + } + jpeg_start_compress(&cinfo, TRUE); + if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) { + jpeg_write_marker(&cinfo, JPEG_COM, + (const JOCTET *)im->tags.tags[comment_entry].data, + im->tags.tags[comment_entry].size); + } + row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */ if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) { @@ -514,6 +662,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);