From 6a87ea666a8474723869ebdc61654a79c340eb4c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 15 Feb 2008 18:07:39 +0000 Subject: [PATCH] cmyk jpeg write --- ChangeLog | 2 ++ TODO | 12 ++--------- include/vips/internal.h | 8 +++++++- libsrc/conversion/im_copy.c | 20 +++++++++++-------- libsrc/conversion/im_jpeg2vips.c | 34 +++++++++++++++++++++++++++----- libsrc/conversion/im_vips2jpeg.c | 31 ++++++++++++++++++----------- libsrc/conversion/im_vips2png.c | 2 +- man/im_jpeg2vips.3 | 17 ++++++++++++++-- 8 files changed, 87 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index c23f34c7..1cf717a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,8 @@ - revert the dynamic wrapping for Python :-( next version! - added VImage::convert2disc (thanks Ole) - you can now set the jpeg quality factor for tiff pyramids (thanks Joe) +- you can now shrink jpegs during read, see "man im_jpeg2vips" +- added CMYK JPEG write 12/12/07 started 7.13.3 - added "include " to VImage.cc to help gcc 4.3 diff --git a/TODO b/TODO index 7a0b8851..f69ab308 100644 --- a/TODO +++ b/TODO @@ -1,17 +1,9 @@ - JPEG write should know about CMYK as well, maybe write all 4 band images as CMYK? -- JPEG read could have a "subsample" param and use - -unsigned int scale_num, scale_denom - Scale the image by the fraction scale_num/scale_denom. - Default is 1/1, or no scaling. Currently, the only supported - scaling ratios are 1/1, 1/2, 1/4, and 1/8. (The library design - allows for arbitrary scaling ratios but this is not likely to - be implemented any time soon.) Smaller scaling ratios permit - significantly faster decoding since fewer pixels need be processed - and a simpler IDCT method can be used. + test CMYK JPEG write, also PNG write with new saveable + can we use convert_saveable for PPM and friends as well? - HAVE_HYPOT could define a hypot() macro? diff --git a/include/vips/internal.h b/include/vips/internal.h index 9d9583b4..57a99270 100644 --- a/include/vips/internal.h +++ b/include/vips/internal.h @@ -77,7 +77,13 @@ extern int im__read_test; extern int im__mmap_limit; extern GMutex *im__global_lock; -IMAGE *im__convert_saveable( IMAGE *in, gboolean allow_alpha ); +typedef enum { + IM__RGB, /* 1 or 3 bands (like PPM) */ + IM__RGBA, /* 1, 2, 3 or 4 bands (like PNG) */ + IM__RGB_CMYK /* 1, 3 or 4 bands (like JPEG) */ +} im__saveable_t; + +IMAGE *im__convert_saveable( IMAGE *in, im__saveable_t saveable ); void im__link_make( IMAGE *parent, IMAGE *child ); void im__link_break_all( IMAGE *im ); diff --git a/libsrc/conversion/im_copy.c b/libsrc/conversion/im_copy.c index acaf6a77..3c2db795 100644 --- a/libsrc/conversion/im_copy.c +++ b/libsrc/conversion/im_copy.c @@ -52,6 +52,8 @@ * 2/11/06 * - moved im__convert_saveable() here so it's always defined (was part * of JPEG write code) + * 15/2/08 + * - added im__saveable_t ... so we can have CMYK JPEG write */ /* @@ -90,6 +92,7 @@ #include #include +#include #ifdef WITH_DMALLOC #include @@ -344,11 +347,12 @@ im_copy_from( IMAGE *in, IMAGE *out, im_arch_type architecture ) } } -/* Convert to 1 or 3 band uchar sRGB (or 2/4 band, if allow_alpha is set). - * Need to im_close() the return IMAGE. +/* Convert to a saveable format. im__saveable_t gives the general type of image + * we make: vanilla 1/3 bands (PPM), with an optional alpha (like PNG), or + * with CMYK as an option (like JPEG). Need to im_close() the return IMAGE. */ IMAGE * -im__convert_saveable( IMAGE *in, gboolean allow_alpha ) +im__convert_saveable( IMAGE *in, im__saveable_t saveable ) { IMAGE *out; @@ -375,11 +379,10 @@ im__convert_saveable( IMAGE *in, gboolean allow_alpha ) in = t; } - /* Get the bands right. If we have >3, drop down to 3. If we have 2, - * drop down to 1. If allow_alpha is on, we can also have 2/4 bands. + /* Get the bands right. */ if( in->Coding == IM_CODING_NONE ) { - if( in->Bands == 2 && !allow_alpha ) { + if( in->Bands == 2 && saveable != IM__RGBA ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); if( !t || im_extract_band( in, t, 0 ) ) { @@ -389,7 +392,7 @@ im__convert_saveable( IMAGE *in, gboolean allow_alpha ) in = t; } - else if( in->Bands > 3 && !allow_alpha ) { + else if( in->Bands > 3 && saveable == IM__RGB ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); if( !t || @@ -400,7 +403,8 @@ im__convert_saveable( IMAGE *in, gboolean allow_alpha ) in = t; } - else if( in->Bands > 4 && allow_alpha ) { + else if( in->Bands > 4 && + (saveable == IM__RGB_CMYK || saveable == IM__RGBA) ) { IMAGE *t = im_open_local( out, "conv:1", "p" ); if( !t || diff --git a/libsrc/conversion/im_jpeg2vips.c b/libsrc/conversion/im_jpeg2vips.c index bf81d51e..a7d17972 100644 --- a/libsrc/conversion/im_jpeg2vips.c +++ b/libsrc/conversion/im_jpeg2vips.c @@ -20,6 +20,8 @@ * 11/2/08 * - spot CMYK jpegs and set Type * - spot Adobe CMYK JPEG and invert ink density + * 15/2/08 + * - added "shrink" parameter */ /* @@ -432,7 +434,7 @@ read_exif( IMAGE *im, void *data, int data_length ) */ static int read_jpeg_header( struct jpeg_decompress_struct *cinfo, - IMAGE *out, gboolean *invert_pels ) + IMAGE *out, gboolean *invert_pels, int shrink ) { jpeg_saved_marker_ptr p; int type; @@ -448,6 +450,7 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo, * for YUV YCCK etc. */ jpeg_read_header( cinfo, TRUE ); + cinfo->scale_denom = shrink; jpeg_calc_output_dimensions( cinfo ); *invert_pels = FALSE; switch( cinfo->out_color_space ) { @@ -611,12 +614,32 @@ read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out, static int jpeg2vips( const char *name, IMAGE *out, gboolean header_only ) { + char filename[FILENAME_MAX]; + char mode[FILENAME_MAX]; + char *p, *q; + int shrink; struct jpeg_decompress_struct cinfo; ErrorManager eman; FILE *fp; int result; gboolean invert_pels; + /* Parse the filename. + */ + im_filename_split( name, filename, mode ); + p = &mode[0]; + shrink = 1; + if( (q = im_getnextoption( &p )) ) { + shrink = atoi( q ); + + if( shrink != 1 && shrink != 2 && + shrink != 4 && shrink != 8 ) { + im_error( "im_jpeg2vips", + _( "bad shrink factor %d" ), shrink ); + return( -1 ); + } + } + /* Make jpeg compression object. */ cinfo.err = jpeg_std_error( &eman.pub ); @@ -635,12 +658,13 @@ jpeg2vips( const char *name, IMAGE *out, gboolean header_only ) /* Make input. */ #ifdef BINARY_OPEN - if( !(fp = fopen( name, "rb" )) ) { + if( !(fp = fopen( filename, "rb" )) ) { #else /*BINARY_OPEN*/ - if( !(fp = fopen( name, "r" )) ) { + if( !(fp = fopen( filename, "r" )) ) { #endif /*BINARY_OPEN*/ jpeg_destroy_decompress( &cinfo ); - im_error( "im_jpeg2vips", _( "unable to open \"%s\"" ), name ); + im_error( "im_jpeg2vips", + _( "unable to open \"%s\"" ), filename ); return( -1 ); } @@ -654,7 +678,7 @@ jpeg2vips( const char *name, IMAGE *out, gboolean header_only ) /* Convert! */ - result = read_jpeg_header( &cinfo, out, &invert_pels ); + result = read_jpeg_header( &cinfo, out, &invert_pels, shrink ); if( !header_only && !result ) result = read_jpeg_image( &cinfo, out, invert_pels ); diff --git a/libsrc/conversion/im_vips2jpeg.c b/libsrc/conversion/im_vips2jpeg.c index e6b3aaf8..8a306c12 100644 --- a/libsrc/conversion/im_vips2jpeg.c +++ b/libsrc/conversion/im_vips2jpeg.c @@ -1,4 +1,4 @@ -/* Convert 1 or 3-band 8-bit VIPS images to/from JPEG. +/* Convert 8-bit VIPS images to/from JPEG. * * 28/11/03 JC * - better no-overshoot on tile loop @@ -23,6 +23,8 @@ * - oop, libexif confusion * 2/11/07 * - use im_wbuffer() API for BG writes + * 15/2/08 + * - write CMYK if Bands == 4 and Type == CMYK */ /* @@ -217,9 +219,9 @@ write_new( IMAGE *in ) return( NULL ); memset( write, 0, sizeof( Write ) ); - if( !(write->in = im__convert_saveable( in, FALSE )) ) { + if( !(write->in = im__convert_saveable( in, IM__RGB_CMYK )) ) { im_error( "im_vips2jpeg", - _( "unable to convert to RGB for save" ) ); + _( "unable to convert to saveable format" ) ); write_destroy( write ); return( NULL ); } @@ -541,12 +543,13 @@ static int write_vips( Write *write, int qfac, const char *profile ) { IMAGE *in = write->in; + J_COLOR_SPACE space; /* Should have been converted for save. */ assert( in->BandFmt == IM_BANDFMT_UCHAR ); assert( in->Coding == IM_CODING_NONE ); - assert( in->Bands == 1 || in->Bands == 3 ); + assert( in->Bands == 1 || in->Bands == 3 || in->Bands == 4 ); /* Check input image. */ @@ -561,14 +564,18 @@ write_vips( Write *write, int qfac, const char *profile ) */ write->cinfo.image_width = in->Xsize; write->cinfo.image_height = in->Ysize; - if( in->Bands == 3 ) { - write->cinfo.input_components = 3; - write->cinfo.in_color_space = JCS_RGB; - } - else if( in->Bands == 1 ) { - write->cinfo.input_components = 1; - write->cinfo.in_color_space = JCS_GRAYSCALE; - } + write->cinfo.input_components = in->Bands; + if( in->Bands == 4 && in->Type == IM_TYPE_CMYK ) + space = JCS_CMYK; + else if( in->Bands == 3 ) + space = JCS_RGB; + else if( in->Bands == 1 ) + space = JCS_GRAYSCALE; + else + /* Use luminance compression for all channels. + */ + space = JCS_UNKNOWN; + write->cinfo.in_color_space = space; /* Rest to default. */ diff --git a/libsrc/conversion/im_vips2png.c b/libsrc/conversion/im_vips2png.c index ecb09f5a..f66803c5 100644 --- a/libsrc/conversion/im_vips2png.c +++ b/libsrc/conversion/im_vips2png.c @@ -125,7 +125,7 @@ write_new( IMAGE *in ) return( NULL ); memset( write, 0, sizeof( Write ) ); - if( !(write->in = im__convert_saveable( in, TRUE )) ) { + if( !(write->in = im__convert_saveable( in, IM__RGBA )) ) { im_error( "im_vips2png", _( "unable to convert to RGB for save" ) ); write_destroy( write ); diff --git a/man/im_jpeg2vips.3 b/man/im_jpeg2vips.3 index 8d5d3adb..f4b7fbdf 100644 --- a/man/im_jpeg2vips.3 +++ b/man/im_jpeg2vips.3 @@ -17,8 +17,21 @@ int im_vips2mimejpeg( IMAGE *in ) .SH DESCRIPTION .B im_jpeg2vips() reads the named jpeg file and writes it to the specified -IMAGE. The entire image is read before returning. It will handle 1 and 3 band -8-bit images only. +IMAGE. The entire image is read before returning. It can read most 8-bit JPEG +images, including CMYK. + +You can embed options in the filename. They have the form: + + filename.jpg: + +.B shrink-factor +will shrink the image by that factor during read. Allowed values are 1, 2, 4 +and 8. Shrinking during read is very much faster than decompressing the whole +image and then shrinking. Example: + + fred.jpg:8 + +will return fred.jpg shrink by a factor of 8. Any embedded ICC profiles are ignored: you always just get the RGB from the file. Instead, the embedded profile will be attached to the image as metadata.