X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=coders%2Fjpeg.c;h=e3fe7a1d343df915a9762c554f7d2cf3957a0a89;hb=3fcfa950eeb4614ffea1dba94a68569bb4491ded;hp=07589a9428a09e3a87faea99c1ff553bbf37c6ff;hpb=524222d4afe8a310858650f263b23cc300d7d792;p=imagemagick diff --git a/coders/jpeg.c b/coders/jpeg.c index 07589a942..e3fe7a1d3 100644 --- a/coders/jpeg.c +++ b/coders/jpeg.c @@ -17,7 +17,7 @@ % July 1992 % % % % % -% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization % +% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % @@ -43,44 +43,49 @@ /* Include declarations. */ -#include "magick/studio.h" -#include "magick/blob.h" -#include "magick/blob-private.h" -#include "magick/cache.h" -#include "magick/color.h" -#include "magick/colormap-private.h" -#include "magick/color-private.h" -#include "magick/colormap.h" -#include "magick/colorspace.h" -#include "magick/constitute.h" -#include "magick/exception.h" -#include "magick/exception-private.h" -#include "magick/geometry.h" -#include "magick/image.h" -#include "magick/image-private.h" -#include "magick/list.h" -#include "magick/log.h" -#include "magick/magick.h" -#include "magick/memory_.h" -#include "magick/module.h" -#include "magick/monitor.h" -#include "magick/monitor-private.h" -#include "magick/option.h" -#include "magick/profile.h" -#include "magick/property.h" -#include "magick/quantum-private.h" -#include "magick/resource_.h" -#include "magick/splay-tree.h" -#include "magick/static.h" -#include "magick/string_.h" -#include "magick/string-private.h" -#include "magick/utility.h" +#include "MagickCore/studio.h" +#include "MagickCore/attribute.h" +#include "MagickCore/blob.h" +#include "MagickCore/blob-private.h" +#include "MagickCore/cache.h" +#include "MagickCore/color.h" +#include "MagickCore/colormap-private.h" +#include "MagickCore/color-private.h" +#include "MagickCore/colormap.h" +#include "MagickCore/colorspace.h" +#include "MagickCore/colorspace-private.h" +#include "MagickCore/constitute.h" +#include "MagickCore/exception.h" +#include "MagickCore/exception-private.h" +#include "MagickCore/geometry.h" +#include "MagickCore/image.h" +#include "MagickCore/image-private.h" +#include "MagickCore/list.h" +#include "MagickCore/log.h" +#include "MagickCore/magick.h" +#include "MagickCore/memory_.h" +#include "MagickCore/module.h" +#include "MagickCore/monitor.h" +#include "MagickCore/monitor-private.h" +#include "MagickCore/option.h" +#include "MagickCore/pixel-accessor.h" +#include "MagickCore/profile.h" +#include "MagickCore/property.h" +#include "MagickCore/quantum-private.h" +#include "MagickCore/resource_.h" +#include "MagickCore/splay-tree.h" +#include "MagickCore/static.h" +#include "MagickCore/string_.h" +#include "MagickCore/string-private.h" +#include "MagickCore/token.h" +#include "MagickCore/utility.h" +#include "MagickCore/xml-tree.h" +#include "MagickCore/xml-tree-private.h" #include #if defined(MAGICKCORE_JPEG_DELEGATE) #define JPEG_INTERNAL_OPTIONS #if defined(__MINGW32__) # define XMD_H 1 /* Avoid conflicting typedef for INT32 */ -typedef unsigned char boolean; #endif #undef HAVE_STDLIB_H #include "jpeglib.h" @@ -94,7 +99,7 @@ typedef unsigned char boolean; #define ICC_PROFILE "ICC_PROFILE" #define IPTC_MARKER (JPEG_APP0+13) #define XML_MARKER (JPEG_APP0+1) -#define MaxBufferExtent 8192 +#define MaxBufferExtent 16384 /* Typedef declarations. @@ -114,9 +119,18 @@ typedef struct _DestinationManager typedef struct _ErrorManager { + ExceptionInfo + *exception; + Image *image; + MagickBooleanType + finished; + + StringInfo + *profile; + jmp_buf error_recovery; } ErrorManager; @@ -136,13 +150,30 @@ typedef struct _SourceManager start_of_blob; } SourceManager; #endif + +typedef struct _QuantizationTable +{ + char + *slot, + *description; + + size_t + width, + height; + + double + divisor; + + unsigned int + *levels; +} QuantizationTable; /* Forward declarations. */ #if defined(MAGICKCORE_JPEG_DELEGATE) static MagickBooleanType - WriteJPEGImage(const ImageInfo *,Image *); + WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *); #endif /* @@ -208,34 +239,6 @@ static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length) % */ -static MagickBooleanType EmitMessage(j_common_ptr jpeg_info,int level) -{ - char - message[JMSG_LENGTH_MAX]; - - ErrorManager - *error_manager; - - Image - *image; - - (jpeg_info->err->format_message)(jpeg_info,message); - error_manager=(ErrorManager *) jpeg_info->client_data; - image=error_manager->image; - if (level < 0) - { - if ((jpeg_info->err->num_warnings == 0) || - (jpeg_info->err->trace_level >= 3)) - ThrowBinaryException(CorruptImageWarning,(char *) message, - image->filename); - jpeg_info->err->num_warnings++; - } - else - if (jpeg_info->err->trace_level >= level) - ThrowBinaryException(CoderError,(char *) message,image->filename); - return(MagickTrue); -} - static boolean FillInputBuffer(j_decompress_ptr cinfo) { SourceManager @@ -246,7 +249,7 @@ static boolean FillInputBuffer(j_decompress_ptr cinfo) MaxBufferExtent,source->buffer); if (source->manager.bytes_in_buffer == 0) { - if (source->start_of_blob != 0) + if (source->start_of_blob != FALSE) ERREXIT(cinfo,JERR_INPUT_EMPTY); WARNMS(cinfo,JWRN_JPEG_EOF); source->buffer[0]=(JOCTET) 0xff; @@ -297,26 +300,92 @@ static MagickBooleanType IsITUFaxImage(const Image *image) static void JPEGErrorHandler(j_common_ptr jpeg_info) { + char + message[JMSG_LENGTH_MAX]; + ErrorManager *error_manager; - (void) EmitMessage(jpeg_info,0); + ExceptionInfo + *exception; + + Image + *image; + + *message='\0'; error_manager=(ErrorManager *) jpeg_info->client_data; + image=error_manager->image; + exception=error_manager->exception; + (jpeg_info->err->format_message)(jpeg_info,message); + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + "[%s] JPEG Trace: \"%s\"",image->filename,message); + if (error_manager->finished != MagickFalse) + (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning, + (char *) message,"`%s'",image->filename); + else + (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError, + (char *) message,"`%s'",image->filename); longjmp(error_manager->error_recovery,1); } -static boolean ReadComment(j_decompress_ptr jpeg_info) +static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level) { +#define JPEGExcessiveWarnings 1000 + char - *comment; + message[JMSG_LENGTH_MAX]; ErrorManager *error_manager; + ExceptionInfo + *exception; + Image *image; - register char + *message='\0'; + error_manager=(ErrorManager *) jpeg_info->client_data; + exception=error_manager->exception; + image=error_manager->image; + if (level < 0) + { + /* + Process warning message. + */ + (jpeg_info->err->format_message)(jpeg_info,message); + if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings) + JPEGErrorHandler(jpeg_info); + ThrowBinaryException(CorruptImageWarning,(char *) message, + image->filename); + } + else + if ((image->debug != MagickFalse) && + (level >= jpeg_info->err->trace_level)) + { + /* + Process trace message. + */ + (jpeg_info->err->format_message)(jpeg_info,message); + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + "[%s] JPEG Trace: \"%s\"",image->filename,message); + } + return(MagickTrue); +} + +static boolean ReadComment(j_decompress_ptr jpeg_info) +{ + ErrorManager + *error_manager; + + ExceptionInfo + *exception; + + Image + *image; + + register unsigned char *p; register ssize_t @@ -325,32 +394,36 @@ static boolean ReadComment(j_decompress_ptr jpeg_info) size_t length; + StringInfo + *comment; + /* Determine length of comment. */ error_manager=(ErrorManager *) jpeg_info->client_data; + exception=error_manager->exception; image=error_manager->image; length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8); length+=GetCharacter(jpeg_info); length-=2; if (length <= 0) return(MagickTrue); - comment=(char *) NULL; - if (~length >= MaxTextExtent) - comment=(char *) AcquireQuantumMemory(length+MaxTextExtent, - sizeof(*comment)); - if (comment == (char *) NULL) + comment=BlobToStringInfo((const void *) NULL,length); + if (comment == (StringInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); /* Read comment. */ - i=(ssize_t) length-1; - for (p=comment; i-- >= 0; p++) - *p=(char) GetCharacter(jpeg_info); + error_manager->profile=comment; + p=GetStringInfoDatum(comment); + for (i=0; i < (ssize_t) GetStringInfoLength(comment); i++) + *p++=(unsigned char) GetCharacter(jpeg_info); *p='\0'; - (void) SetImageProperty(image,"comment",comment); - comment=DestroyString(comment); + error_manager->profile=NULL; + p=GetStringInfoDatum(comment); + (void) SetImageProperty(image,"comment",(const char *) p,exception); + comment=DestroyStringInfo(comment); return(MagickTrue); } @@ -362,6 +435,9 @@ static boolean ReadICCProfile(j_decompress_ptr jpeg_info) ErrorManager *error_manager; + ExceptionInfo + *exception; + Image *image; @@ -408,14 +484,17 @@ static boolean ReadICCProfile(j_decompress_ptr jpeg_info) (void) GetCharacter(jpeg_info); /* markers */ length-=14; error_manager=(ErrorManager *) jpeg_info->client_data; + exception=error_manager->exception; image=error_manager->image; - profile=AcquireStringInfo(length); + profile=BlobToStringInfo((const void *) NULL,length); if (profile == (StringInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); + error_manager->profile=profile; p=GetStringInfoDatum(profile); for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--) *p++=(unsigned char) GetCharacter(jpeg_info); + error_manager->profile=NULL; icc_profile=(StringInfo *) GetImageProfile(image,"icc"); if (icc_profile != (StringInfo *) NULL) { @@ -424,7 +503,7 @@ static boolean ReadICCProfile(j_decompress_ptr jpeg_info) } else { - status=SetImageProfile(image,"icc",profile); + status=SetImageProfile(image,"icc",profile,exception); profile=DestroyStringInfo(profile); if (status == MagickFalse) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", @@ -444,6 +523,9 @@ static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info) ErrorManager *error_manager; + ExceptionInfo + *exception; + Image *image; @@ -504,14 +586,17 @@ static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info) if (length == 0) return(MagickTrue); error_manager=(ErrorManager *) jpeg_info->client_data; + exception=error_manager->exception; image=error_manager->image; - profile=AcquireStringInfo(length); + profile=BlobToStringInfo((const void *) NULL,length); if (profile == (StringInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); + error_manager->profile=profile; p=GetStringInfoDatum(profile); for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++) *p++=(unsigned char) GetCharacter(jpeg_info); + error_manager->profile=NULL; iptc_profile=(StringInfo *) GetImageProfile(image,"8bim"); if (iptc_profile != (StringInfo *) NULL) { @@ -520,7 +605,7 @@ static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info) } else { - status=SetImageProfile(image,"8bim",profile); + status=SetImageProfile(image,"8bim",profile,exception); profile=DestroyStringInfo(profile); if (status == MagickFalse) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", @@ -537,9 +622,15 @@ static boolean ReadProfile(j_decompress_ptr jpeg_info) char name[MaxTextExtent]; + const StringInfo + *previous_profile; + ErrorManager *error_manager; + ExceptionInfo + *exception; + Image *image; @@ -570,16 +661,19 @@ static boolean ReadProfile(j_decompress_ptr jpeg_info) return(MagickTrue); length-=2; marker=jpeg_info->unread_marker-JPEG_APP0; - (void) FormatMagickString(name,MaxTextExtent,"APP%d",marker); + (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker); error_manager=(ErrorManager *) jpeg_info->client_data; + exception=error_manager->exception; image=error_manager->image; - profile=AcquireStringInfo(length); + profile=BlobToStringInfo((const void *) NULL,length); if (profile == (StringInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); + error_manager->profile=profile; p=GetStringInfoDatum(profile); - for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--) + for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++) *p++=(unsigned char) GetCharacter(jpeg_info); + error_manager->profile=NULL; if (marker == 1) { p=GetStringInfoDatum(profile); @@ -605,7 +699,23 @@ static boolean ReadProfile(j_decompress_ptr jpeg_info) (void) CopyMagickString(name,"xmp",MaxTextExtent); } } - status=SetImageProfile(image,name,profile); + previous_profile=GetImageProfile(image,name); + if (previous_profile != (const StringInfo *) NULL) + { + size_t + length; + + length=GetStringInfoLength(profile); + SetStringInfoLength(profile,GetStringInfoLength(profile)+ + GetStringInfoLength(previous_profile)); + (void) memmove(GetStringInfoDatum(profile)+ + GetStringInfoLength(previous_profile),GetStringInfoDatum(profile), + length); + (void) memcpy(GetStringInfoDatum(profile), + GetStringInfoDatum(previous_profile), + GetStringInfoLength(previous_profile)); + } + status=SetImageProfile(image,name,profile,exception); profile=DestroyStringInfo(profile); if (status == MagickFalse) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", @@ -635,7 +745,7 @@ static void SkipInputData(j_decompress_ptr cinfo,long number_bytes) static void TerminateSource(j_decompress_ptr cinfo) { - cinfo=cinfo; + (void) cinfo; } static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image) @@ -794,7 +904,7 @@ static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info, } } -static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image) +static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception) { char sampling_factor[MaxTextExtent]; @@ -804,7 +914,7 @@ static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, case JCS_CMYK: { (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK"); - (void) FormatMagickString(sampling_factor,MaxTextExtent, + (void) FormatLocaleString(sampling_factor,MaxTextExtent, "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor, jpeg_info->comp_info[0].v_samp_factor, jpeg_info->comp_info[1].h_samp_factor, @@ -819,7 +929,7 @@ static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, { (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Colorspace: GRAYSCALE"); - (void) FormatMagickString(sampling_factor,MaxTextExtent,"%dx%d", + (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d", jpeg_info->comp_info[0].h_samp_factor, jpeg_info->comp_info[0].v_samp_factor); break; @@ -827,7 +937,7 @@ static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, case JCS_RGB: { (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB"); - (void) FormatMagickString(sampling_factor,MaxTextExtent, + (void) FormatLocaleString(sampling_factor,MaxTextExtent, "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor, jpeg_info->comp_info[0].v_samp_factor, jpeg_info->comp_info[1].h_samp_factor, @@ -840,7 +950,7 @@ static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, { (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d", jpeg_info->out_color_space); - (void) FormatMagickString(sampling_factor,MaxTextExtent, + (void) FormatLocaleString(sampling_factor,MaxTextExtent, "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor, jpeg_info->comp_info[0].v_samp_factor, jpeg_info->comp_info[1].h_samp_factor, @@ -852,7 +962,8 @@ static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, break; } } - (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor); + (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor, + exception); (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s", sampling_factor); } @@ -872,11 +983,8 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, Image *image; - IndexPacket - index; - JSAMPLE - *jpeg_pixels; + *volatile jpeg_pixels; JSAMPROW scanline[1]; @@ -888,6 +996,9 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, MagickSizeType number_pixels; + Quantum + index; + register ssize_t i; @@ -919,7 +1030,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, assert(exception->signature == MagickSignature); debug=IsEventLogging(); (void) debug; - image=AcquireImage(image_info); + image=AcquireImage(image_info,exception); status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == MagickFalse) { @@ -933,18 +1044,20 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info)); (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error)); jpeg_info.err=jpeg_std_error(&jpeg_error); - jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage; + jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler; jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler; jpeg_pixels=(JSAMPLE *) NULL; + error_manager.exception=exception; error_manager.image=image; if (setjmp(error_manager.error_recovery) != 0) { jpeg_destroy_decompress(&jpeg_info); + if (error_manager.profile != (StringInfo *) NULL) + error_manager.profile=DestroyStringInfo(error_manager.profile); (void) CloseBlob(image); number_pixels=(MagickSizeType) image->columns*image->rows; if (number_pixels != 0) return(GetFirstImageInList(image)); - InheritException(exception,&image->exception); return(DestroyImage(image)); } jpeg_info.client_data=(void *) &error_manager; @@ -961,14 +1074,6 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, (image_info->colorspace == Rec601YCbCrColorspace) || (image_info->colorspace == Rec709YCbCrColorspace)) jpeg_info.out_color_space=JCS_YCbCr; - if (IsITUFaxImage(image) != MagickFalse) - { - image->colorspace=LabColorspace; - jpeg_info.out_color_space=JCS_YCbCr; - } - else - if (jpeg_info.out_color_space == JCS_CMYK) - image->colorspace=CMYKColorspace; /* Set image resolution. */ @@ -976,8 +1081,8 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) && (jpeg_info.Y_density != 1)) { - image->x_resolution=(double) jpeg_info.X_density; - image->y_resolution=(double) jpeg_info.Y_density; + image->resolution.x=(double) jpeg_info.X_density; + image->resolution.y=(double) jpeg_info.Y_density; units=(size_t) jpeg_info.density_unit; } if (units == 1) @@ -1041,21 +1146,18 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, image->compression=JPEGCompression; image->interlace=JPEGInterlace; #endif - if ((image_info->colors > 8) && (image_info->colors <= 256)) + option=GetImageOption(image_info,"jpeg:colors"); + if (option != (const char *) NULL) { /* - Let the JPEG library quantize for us. + Let the JPEG library quantize the image. */ jpeg_info.quantize_colors=MagickTrue; - jpeg_info.desired_number_of_colors=(int) image_info->colors; + jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option); } option=GetImageOption(image_info,"jpeg:block-smoothing"); - if (option != (const char *) NULL) - { - jpeg_info.do_block_smoothing=MagickFalse; - if (IsMagickTrue(option) != MagickFalse) - jpeg_info.do_block_smoothing=MagickTrue; - } + jpeg_info.do_block_smoothing=IsStringTrue(option); + jpeg_info.dct_method=JDCT_FLOAT; option=GetImageOption(image_info,"jpeg:dct-method"); if (option != (const char *) NULL) switch (*option) @@ -1087,22 +1189,44 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, } } option=GetImageOption(image_info,"jpeg:fancy-upsampling"); - if (option != (const char *) NULL) - { - jpeg_info.do_fancy_upsampling=MagickFalse; - if (IsMagickTrue(option) != MagickFalse) - jpeg_info.do_fancy_upsampling=MagickTrue; - } + jpeg_info.do_fancy_upsampling=IsStringTrue(option); (void) jpeg_start_decompress(&jpeg_info); image->columns=jpeg_info.output_width; image->rows=jpeg_info.output_height; image->depth=(size_t) jpeg_info.data_precision; - if (jpeg_info.out_color_space == JCS_YCbCr) - image->colorspace=YCbCrColorspace; - if (jpeg_info.out_color_space == JCS_CMYK) - image->colorspace=CMYKColorspace; - if ((image_info->colors != 0) && (image_info->colors <= 256)) - if (AcquireImageColormap(image,image_info->colors) == MagickFalse) + switch (jpeg_info.out_color_space) + { + case JCS_RGB: + default: + { + (void) SetImageColorspace(image,sRGBColorspace,exception); + break; + } + case JCS_GRAYSCALE: + { + (void) SetImageColorspace(image,GRAYColorspace,exception); + break; + } + case JCS_YCbCr: + { + (void) SetImageColorspace(image,YCbCrColorspace,exception); + break; + } + case JCS_CMYK: + { + (void) SetImageColorspace(image,CMYKColorspace,exception); + break; + } + } + if (IsITUFaxImage(image) != MagickFalse) + { + (void) SetImageColorspace(image,LabColorspace,exception); + jpeg_info.out_color_space=JCS_YCbCr; + } + option=GetImageOption(image_info,"jpeg:colors"); + if (option != (const char *) NULL) + if (AcquireImageColormap(image,StringToUnsignedLong(option),exception) + == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); if ((jpeg_info.output_components == 1) && (jpeg_info.quantize_colors == MagickFalse)) @@ -1111,7 +1235,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, colors; colors=(size_t) GetQuantumRange(image->depth)+1; - if (AcquireImageColormap(image,colors) == MagickFalse) + if (AcquireImageColormap(image,colors,exception) == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } if (image->debug != MagickFalse) @@ -1128,10 +1252,10 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, (int) jpeg_info.output_width,(int) jpeg_info.output_height); } JPEGSetImageQuality(&jpeg_info,image); - JPEGSetImageSamplingFactor(&jpeg_info,image); - (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double) + JPEGSetImageSamplingFactor(&jpeg_info,image,exception); + (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) jpeg_info.out_color_space); - (void) SetImageProperty(image,"jpeg:colorspace",value); + (void) SetImageProperty(image,"jpeg:colorspace",value,exception); if (image_info->ping != MagickFalse) { jpeg_destroy_decompress(&jpeg_info); @@ -1147,8 +1271,8 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, */ if (setjmp(error_manager.error_recovery) != 0) { - if (jpeg_pixels != (unsigned char *) NULL) - jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); + if (jpeg_pixels != (JSAMPLE *) NULL) + jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels); jpeg_destroy_decompress(&jpeg_info); (void) CloseBlob(image); number_pixels=(MagickSizeType) image->columns*image->rows; @@ -1162,30 +1286,31 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, if (jpeg_info.out_color_space == JCS_GRAYSCALE) for (i=0; i < (ssize_t) image->colors; i++) { - image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]); + image->colormap[i].red=(double) ScaleCharToQuantum( + jpeg_info.colormap[0][i]); image->colormap[i].green=image->colormap[i].red; image->colormap[i].blue=image->colormap[i].red; - image->colormap[i].opacity=OpaqueOpacity; + image->colormap[i].alpha=OpaqueAlpha; } else for (i=0; i < (ssize_t) image->colors; i++) { - image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]); - image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]); - image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]); - image->colormap[i].opacity=OpaqueOpacity; + image->colormap[i].red=(double) ScaleCharToQuantum( + jpeg_info.colormap[0][i]); + image->colormap[i].green=(double) ScaleCharToQuantum( + jpeg_info.colormap[1][i]); + image->colormap[i].blue=(double) ScaleCharToQuantum( + jpeg_info.colormap[2][i]); + image->colormap[i].alpha=OpaqueAlpha; } } scanline[0]=(JSAMPROW) jpeg_pixels; for (y=0; y < (ssize_t) image->rows; y++) { - register IndexPacket - *restrict indexes; - register ssize_t x; - register PixelPacket + register Quantum *restrict q; if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1) @@ -1196,9 +1321,8 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, } p=jpeg_pixels; q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); - if (q == (PixelPacket *) NULL) + if (q == (Quantum *) NULL) break; - indexes=GetAuthenticIndexQueue(image); if (jpeg_info.data_precision > 8) { if (jpeg_info.output_components == 1) @@ -1211,94 +1335,98 @@ static Image *ReadJPEGImage(const ImageInfo *image_info, pixel=(size_t) GETJSAMPLE(*p); else pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4); - index=ConstrainColormapIndex(image,pixel); - SetIndexPixelComponent(indexes+x,index); - SetRedPixelComponent(q,image->colormap[(int) index].red); - SetGreenPixelComponent(q,image->colormap[(int) index].green); - SetBluePixelComponent(q,image->colormap[(int) index].blue); + index=ConstrainColormapIndex(image,pixel,exception); + SetPixelIndex(image,index,q); + SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q); p++; - q++; + q+=GetPixelChannels(image); } else if (image->colorspace != CMYKColorspace) for (x=0; x < (ssize_t) image->columns; x++) { - SetRedPixelComponent(q,ScaleShortToQuantum((unsigned char) - (GETJSAMPLE(*p++) << 4))); - SetGreenPixelComponent(q,ScaleShortToQuantum((unsigned char) - (GETJSAMPLE(*p++) << 4))); - SetBluePixelComponent(q,ScaleShortToQuantum((unsigned char) - (GETJSAMPLE(*p++) << 4))); - SetOpacityPixelComponent(q,OpaqueOpacity); - q++; + SetPixelRed(image,ScaleShortToQuantum((unsigned char) + (GETJSAMPLE(*p++) << 4)),q); + SetPixelGreen(image,ScaleShortToQuantum((unsigned char) + (GETJSAMPLE(*p++) << 4)),q); + SetPixelBlue(image,ScaleShortToQuantum((unsigned char) + (GETJSAMPLE(*p++) << 4)),q); + SetPixelAlpha(image,OpaqueAlpha,q); + q+=GetPixelChannels(image); } else for (x=0; x < (ssize_t) image->columns; x++) { - SetCyanPixelComponent(q,QuantumRange-ScaleShortToQuantum( - (unsigned char) (GETJSAMPLE(*p++) << 4))); - SetMagentaPixelComponent(q,QuantumRange-ScaleShortToQuantum( - (unsigned char) (GETJSAMPLE(*p++) << 4))); - SetYellowPixelComponent(q,QuantumRange-ScaleShortToQuantum( - (unsigned char) (GETJSAMPLE(*p++) << 4))); - SetBlackPixelComponent(indexes+x,QuantumRange-ScaleShortToQuantum( - (unsigned char) (GETJSAMPLE(*p++) << 4))); - SetOpacityPixelComponent(q,OpaqueOpacity); - q++; + SetPixelCyan(image,QuantumRange-ScaleShortToQuantum( + (unsigned char) (GETJSAMPLE(*p++) << 4)),q); + SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum( + (unsigned char) (GETJSAMPLE(*p++) << 4)),q); + SetPixelYellow(image,QuantumRange-ScaleShortToQuantum( + (unsigned char) (GETJSAMPLE(*p++) << 4)),q); + SetPixelBlack(image,QuantumRange-ScaleShortToQuantum( + (unsigned char) (GETJSAMPLE(*p++) << 4)),q); + SetPixelAlpha(image,OpaqueAlpha,q); + q+=GetPixelChannels(image); } } else if (jpeg_info.output_components == 1) for (x=0; x < (ssize_t) image->columns; x++) { - index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p)); - SetIndexPixelComponent(indexes+x,index); - SetRedPixelComponent(q,image->colormap[(int) index].red); - SetGreenPixelComponent(q,image->colormap[(int) index].green); - SetBluePixelComponent(q,image->colormap[(int) index].blue); + index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception); + SetPixelIndex(image,index,q); + SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q); p++; - q++; + q+=GetPixelChannels(image); } else if (image->colorspace != CMYKColorspace) for (x=0; x < (ssize_t) image->columns; x++) { - SetRedPixelComponent(q,ScaleCharToQuantum((unsigned char) - GETJSAMPLE(*p++))); - SetGreenPixelComponent(q,ScaleCharToQuantum((unsigned char) - GETJSAMPLE(*p++))); - SetBluePixelComponent(q,ScaleCharToQuantum((unsigned char) - GETJSAMPLE(*p++))); - SetOpacityPixelComponent(q,OpaqueOpacity); - q++; + SetPixelRed(image,ScaleCharToQuantum((unsigned char) + GETJSAMPLE(*p++)),q); + SetPixelGreen(image,ScaleCharToQuantum((unsigned char) + GETJSAMPLE(*p++)),q); + SetPixelBlue(image,ScaleCharToQuantum((unsigned char) + GETJSAMPLE(*p++)),q); + SetPixelAlpha(image,OpaqueAlpha,q); + q+=GetPixelChannels(image); } else for (x=0; x < (ssize_t) image->columns; x++) { - SetCyanPixelComponent(q,QuantumRange-ScaleCharToQuantum( - (unsigned char) GETJSAMPLE(*p++))); - SetMagentaPixelComponent(q,QuantumRange-ScaleCharToQuantum( - (unsigned char) GETJSAMPLE(*p++))); - SetYellowPixelComponent(q,QuantumRange-ScaleCharToQuantum( - (unsigned char) GETJSAMPLE(*p++))); - SetBlackPixelComponent(indexes+x,QuantumRange-ScaleCharToQuantum( - (unsigned char) GETJSAMPLE(*p++))); - SetOpacityPixelComponent(q,OpaqueOpacity); - q++; + SetPixelCyan(image,QuantumRange-ScaleCharToQuantum( + (unsigned char) GETJSAMPLE(*p++)),q); + SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum( + (unsigned char) GETJSAMPLE(*p++)),q); + SetPixelYellow(image,QuantumRange-ScaleCharToQuantum( + (unsigned char) GETJSAMPLE(*p++)),q); + SetPixelBlack(image,QuantumRange-ScaleCharToQuantum( + (unsigned char) GETJSAMPLE(*p++)),q); + SetPixelAlpha(image,OpaqueAlpha,q); + q+=GetPixelChannels(image); } if (SyncAuthenticPixels(image,exception) == MagickFalse) break; status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, image->rows); if (status == MagickFalse) - break; + { + jpeg_abort_decompress(&jpeg_info); + break; + } } + if (status != MagickFalse) + { + error_manager.finished=MagickTrue; + if (setjmp(error_manager.error_recovery) == 0) + (void) jpeg_finish_decompress(&jpeg_info); + } /* Free jpeg resources. */ - (void) jpeg_finish_decompress(&jpeg_info); jpeg_destroy_decompress(&jpeg_info); - jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); + jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels); (void) CloseBlob(image); return(GetFirstImageInList(image)); } @@ -1340,7 +1468,7 @@ ModuleExport size_t RegisterJPEGImage(void) *version='\0'; #if defined(JPEG_LIB_VERSION) - (void) FormatMagickString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION); + (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION); #endif entry=SetMagickInfo("JPEG"); entry->thread_support=NoThreadSupport; @@ -1427,7 +1555,7 @@ ModuleExport void UnregisterJPEGImage(void) % The format of the WriteJPEGImage method is: % % MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, -% Image *image) +% Image *image,ExceptionInfo *exception) % % A description of each parameter follows: % @@ -1435,9 +1563,23 @@ ModuleExport void UnregisterJPEGImage(void) % % o jpeg_image: The image. % +% o exception: return any errors or warnings in this structure. % */ +static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table) +{ + assert(table != (QuantizationTable *) NULL); + if (table->slot != (char *) NULL) + table->slot=DestroyString(table->slot); + if (table->description != (char *) NULL) + table->description=DestroyString(table->description); + if (table->levels != (unsigned int *) NULL) + table->levels=(unsigned int *) RelinquishMagickMemory(table->levels); + table=(QuantizationTable *) RelinquishMagickMemory(table); + return(table); +} + static boolean EmptyOutputBuffer(j_compress_ptr cinfo) { DestinationManager @@ -1452,6 +1594,203 @@ static boolean EmptyOutputBuffer(j_compress_ptr cinfo) return(TRUE); } +static QuantizationTable *GetQuantizationTable(const char *filename, + const char *slot,ExceptionInfo *exception) +{ + char + *p, + *xml; + + const char + *attribute, + *content; + + double + value; + + register ssize_t + i; + + ssize_t + j; + + QuantizationTable + *table; + + size_t + length; + + XMLTreeInfo + *description, + *levels, + *quantization_tables, + *table_iterator; + + (void) LogMagickEvent(ConfigureEvent,GetMagickModule(), + "Loading quantization tables \"%s\" ...",filename); + table=(QuantizationTable *) NULL; + xml=FileToString(filename,~0,exception); + if (xml == (char *) NULL) + return(table); + quantization_tables=NewXMLTree(xml,exception); + if (quantization_tables == (XMLTreeInfo *) NULL) + { + xml=DestroyString(xml); + return(table); + } + for (table_iterator=GetXMLTreeChild(quantization_tables,"table"); + table_iterator != (XMLTreeInfo *) NULL; + table_iterator=GetNextXMLTreeTag(table_iterator)) + { + attribute=GetXMLTreeAttribute(table_iterator,"slot"); + if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0)) + break; + attribute=GetXMLTreeAttribute(table_iterator,"alias"); + if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0)) + break; + } + if (table_iterator == (XMLTreeInfo *) NULL) + { + xml=DestroyString(xml); + return(table); + } + description=GetXMLTreeChild(table_iterator,"description"); + if (description == (XMLTreeInfo *) NULL) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlMissingElement", ", slot \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + xml=DestroyString(xml); + return(table); + } + levels=GetXMLTreeChild(table_iterator,"levels"); + if (levels == (XMLTreeInfo *) NULL) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlMissingElement", ", slot \"%s\"", slot); + quantization_tables=DestroyXMLTree(quantization_tables); + xml=DestroyString(xml); + return(table); + } + table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table)); + if (table == (QuantizationTable *) NULL) + ThrowFatalException(ResourceLimitFatalError, + "UnableToAcquireQuantizationTable"); + table->slot=(char *) NULL; + table->description=(char *) NULL; + table->levels=(unsigned int *) NULL; + attribute=GetXMLTreeAttribute(table_iterator,"slot"); + if (attribute != (char *) NULL) + table->slot=ConstantString(attribute); + content=GetXMLTreeContent(description); + if (content != (char *) NULL) + table->description=ConstantString(content); + attribute=GetXMLTreeAttribute(levels,"width"); + if (attribute == (char *) NULL) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlMissingAttribute", ", slot \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + table->width=StringToUnsignedLong(attribute); + if (table->width == 0) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlInvalidAttribute", ", table \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + attribute=GetXMLTreeAttribute(levels,"height"); + if (attribute == (char *) NULL) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlMissingAttribute", ", table \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + table->height=StringToUnsignedLong(attribute); + if (table->height == 0) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlInvalidAttribute", ", table \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + attribute=GetXMLTreeAttribute(levels,"divisor"); + if (attribute == (char *) NULL) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlMissingAttribute", ", table \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + table->divisor=InterpretLocaleValue(attribute,(char **) NULL); + if (table->divisor == 0.0) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlInvalidAttribute", ", table \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + content=GetXMLTreeContent(levels); + if (content == (char *) NULL) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlMissingContent", ", table \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + length=(size_t) table->width*table->height; + if (length < 64) + length=64; + table->levels=(unsigned int *) AcquireQuantumMemory(length, + sizeof(*table->levels)); + if (table->levels == (unsigned int *) NULL) + ThrowFatalException(ResourceLimitFatalError, + "UnableToAcquireQuantizationTable"); + for (i=0; i < (ssize_t) (table->width*table->height); i++) + { + table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/ + table->divisor+0.5); + while (isspace((int) ((unsigned char) *p)) != 0) + p++; + if (*p == ',') + p++; + content=p; + } + value=InterpretLocaleValue(content,&p); + (void) value; + if (p != content) + { + (void) ThrowMagickException(exception,GetMagickModule(),OptionError, + "XmlInvalidContent", " too many values, table \"%s\"",slot); + quantization_tables=DestroyXMLTree(quantization_tables); + table=DestroyQuantizationTable(table); + xml=DestroyString(xml); + return(table); + } + for (j=i; j < 64; j++) + table->levels[j]=table->levels[j-1]; + quantization_tables=DestroyXMLTree(quantization_tables); + xml=DestroyString(xml); + return(table); +} + static void InitializeDestination(j_compress_ptr cinfo) { DestinationManager @@ -1589,7 +1928,7 @@ static void WriteProfile(j_compress_ptr jpeg_info,Image *image) /* Add namespace to XMP profile. */ - xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/"); + xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ "); ConcatenateStringInfo(xmp_profile,profile); GetStringInfoDatum(xmp_profile)[28]='\0'; for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L) @@ -1671,7 +2010,7 @@ static char **SamplingFactorToList(const char *text) } static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, - Image *image) + Image *image,ExceptionInfo *exception) { const char *option, @@ -1681,8 +2020,11 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, ErrorManager error_manager; + int + quality; + JSAMPLE - *jpeg_pixels; + *volatile jpeg_pixels; JSAMPROW scanline[1]; @@ -1714,7 +2056,9 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); - status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); + assert(exception != (ExceptionInfo *) NULL); + assert(exception->signature == MagickSignature); + status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); /* @@ -1725,8 +2069,9 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error)); jpeg_info.client_data=(void *) image; jpeg_info.err=jpeg_std_error(&jpeg_error); - jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage; + jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler; jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler; + error_manager.exception=exception; error_manager.image=image; jpeg_pixels=(JSAMPLE *) NULL; if (setjmp(error_manager.error_recovery) != 0) @@ -1771,13 +2116,13 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, } default: { - if (image->colorspace != RGBColorspace) - (void) TransformImageColorspace(image,RGBColorspace); + if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) + (void) TransformImageColorspace(image,sRGBColorspace,exception); break; } } if ((image_info->type != TrueColorType) && - (IsGrayImage(image,&image->exception) != MagickFalse)) + (IsImageGray(image,exception) != MagickFalse)) { jpeg_info.input_components=1; jpeg_info.in_color_space=JCS_GRAYSCALE; @@ -1791,21 +2136,22 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, jpeg_info.density_unit=(UINT8) 1; if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - "Image resolution: %.20g,%.20g",floor(image->x_resolution+0.5), - floor(image->y_resolution+0.5)); - if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0)) + "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5), + floor(image->resolution.y+0.5)); + if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0)) { /* Set image resolution. */ jpeg_info.write_JFIF_header=MagickTrue; - jpeg_info.X_density=(UINT16) floor(image->x_resolution+0.5); - jpeg_info.Y_density=(UINT16) floor(image->y_resolution+0.5); + jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5); + jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5); if (image->units == PixelsPerInchResolution) jpeg_info.density_unit=(UINT8) 1; if (image->units == PixelsPerCentimeterResolution) jpeg_info.density_unit=(UINT8) 2; } + jpeg_info.dct_method=JDCT_FLOAT; option=GetImageOption(image_info,"jpeg:dct-method"); if (option != (const char *) NULL) switch (*option) @@ -1838,11 +2184,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, } option=GetImageOption(image_info,"jpeg:optimize-coding"); if (option != (const char *) NULL) - { - jpeg_info.optimize_coding=MagickFalse; - if (IsMagickTrue(option) != MagickFalse) - jpeg_info.optimize_coding=MagickTrue; - } + jpeg_info.optimize_coding=IsStringTrue(option); else { MagickSizeType @@ -1856,9 +2198,8 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, Perform optimization only if available memory resources permit it. */ status=AcquireMagickResource(MemoryResource,length); - if (status != MagickFalse) - jpeg_info.optimize_coding=MagickTrue; RelinquishMagickResource(MemoryResource,length); + jpeg_info.optimize_coding=status; } } #if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED) @@ -1889,7 +2230,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, *jpeg_info; jpeg_info=CloneImageInfo(image_info); - jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception); + jpeg_image=CloneImage(image,0,0,MagickTrue,exception); if (jpeg_image != (Image *) NULL) { MagickSizeType @@ -1903,14 +2244,14 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, Search for compression quality that does not exceed image extent. */ jpeg_info->quality=0; - extent=(MagickSizeType) SiPrefixToDouble(option,100.0); + extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0); (void) DeleteImageOption(jpeg_info,"jpeg:extent"); (void) AcquireUniqueFilename(jpeg_image->filename); maximum=101; for (minimum=0; minimum != maximum; ) { jpeg_image->quality=minimum+(maximum-minimum)/2; - status=WriteJPEGImage(jpeg_info,jpeg_image); + status=WriteJPEGImage(jpeg_info,jpeg_image,exception); if (GetBlobSize(jpeg_image) <= extent) minimum=jpeg_image->quality+1; else @@ -1922,13 +2263,12 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, } jpeg_info=DestroyImageInfo(jpeg_info); } + quality=92; if ((image_info->compression != LosslessJPEGCompression) && (image->quality <= 100)) { - if (image->quality == UndefinedCompressionQuality) - jpeg_set_quality(&jpeg_info,92,MagickTrue); - else - jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue); + if (image->quality != UndefinedCompressionQuality) + quality=(int) image->quality; if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g", (double) image->quality); @@ -1936,13 +2276,13 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, else { #if !defined(C_LOSSLESS_SUPPORTED) - jpeg_set_quality(&jpeg_info,100,MagickTrue); + quality=100; if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100"); #else if (image->quality < 100) - (void) ThrowMagickException(&image->exception,GetMagickModule(), - CoderWarning,"LosslessToLossyJPEGConversion",image->filename); + (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning, + "LosslessToLossyJPEGConversion",image->filename); else { int @@ -1964,8 +2304,35 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, } #endif } + jpeg_set_quality(&jpeg_info,quality,MagickTrue); +#if (JPEG_LIB_VERSION >= 70) + option=GetImageOption(image_info,"quality"); + if (option != (const char *) NULL) + { + GeometryInfo + geometry_info; + + int + flags; + + /* + Set quality scaling for luminance and chrominance separately. + */ + flags=ParseGeometry(option,&geometry_info); + if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0)) + { + jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int) + (geometry_info.rho+0.5)); + jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int) + (geometry_info.sigma+0.5)); + jpeg_default_qtables(&jpeg_info,MagickTrue); + } + } +#endif sampling_factor=(const char *) NULL; - value=GetImageProperty(image,"jpeg:sampling-factor"); + value=GetImageOption(image_info,"jpeg:sampling-factor"); + if (value == (char *) NULL) + value=GetImageProperty(image,"jpeg:sampling-factor",exception); if (value != (char *) NULL) { sampling_factor=value; @@ -2027,6 +2394,44 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, jpeg_info.comp_info[i].h_samp_factor=1; jpeg_info.comp_info[i].v_samp_factor=1; } + option=GetImageOption(image_info,"jpeg:q-table"); + if (option != (const char *) NULL) + { + QuantizationTable + *table; + + /* + Custom quantization tables. + */ + table=GetQuantizationTable(option,"0",exception); + if (table != (QuantizationTable *) NULL) + { + jpeg_add_quant_table(&jpeg_info,0,table->levels,jpeg_quality_scaling( + quality),0); + table=DestroyQuantizationTable(table); + } + table=GetQuantizationTable(option,"1",exception); + if (table != (QuantizationTable *) NULL) + { + jpeg_add_quant_table(&jpeg_info,1,table->levels,jpeg_quality_scaling( + quality),0); + table=DestroyQuantizationTable(table); + } + table=GetQuantizationTable(option,"2",exception); + if (table != (QuantizationTable *) NULL) + { + jpeg_add_quant_table(&jpeg_info,2,table->levels,jpeg_quality_scaling( + quality),0); + table=DestroyQuantizationTable(table); + } + table=GetQuantizationTable(option,"3",exception); + if (table != (QuantizationTable *) NULL) + { + jpeg_add_quant_table(&jpeg_info,3,table->levels,jpeg_quality_scaling( + quality),0); + table=DestroyQuantizationTable(table); + } + } jpeg_start_compress(&jpeg_info,MagickTrue); if (image->debug != MagickFalse) { @@ -2147,7 +2552,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, /* Write JPEG profiles. */ - value=GetImageProperty(image,"comment"); + value=GetImageProperty(image,"comment",exception); if (value != (char *) NULL) for (i=0; i < (ssize_t) strlen(value); i+=65533L) jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i, @@ -2164,8 +2569,8 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, if (setjmp(error_manager.error_recovery) != 0) { jpeg_destroy_compress(&jpeg_info); - if (jpeg_pixels != (unsigned char *) NULL) - jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); + if (jpeg_pixels != (JSAMPLE *) NULL) + jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels); (void) CloseBlob(image); return(MagickFalse); } @@ -2176,22 +2581,22 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, (jpeg_info.in_color_space == JCS_YCbCr)) for (y=0; y < (ssize_t) image->rows; y++) { - register const PixelPacket + register const Quantum *p; register ssize_t x; - p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception); - if (p == (const PixelPacket *) NULL) + p=GetVirtualPixels(image,0,y,image->columns,1,exception); + if (p == (const Quantum *) NULL) break; q=jpeg_pixels; for (x=0; x < (ssize_t) image->columns; x++) { - *q++=(JSAMPLE) ScaleQuantumToChar(GetRedPixelComponent(p)); - *q++=(JSAMPLE) ScaleQuantumToChar(GetGreenPixelComponent(p)); - *q++=(JSAMPLE) ScaleQuantumToChar(GetBluePixelComponent(p)); - p++; + *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p)); + *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p)); + *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p)); + p+=GetPixelChannels(image); } (void) jpeg_write_scanlines(&jpeg_info,scanline,1); status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, @@ -2203,20 +2608,20 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, if (jpeg_info.in_color_space == JCS_GRAYSCALE) for (y=0; y < (ssize_t) image->rows; y++) { - register const PixelPacket + register const Quantum *p; register ssize_t x; - p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception); - if (p == (const PixelPacket *) NULL) + p=GetVirtualPixels(image,0,y,image->columns,1,exception); + if (p == (const Quantum *) NULL) break; q=jpeg_pixels; for (x=0; x < (ssize_t) image->columns; x++) { - *q++=(JSAMPLE) ScaleQuantumToChar(PixelIntensityToQuantum(p)); - p++; + *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p)); + p+=GetPixelChannels(image); } (void) jpeg_write_scanlines(&jpeg_info,scanline,1); status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, @@ -2227,34 +2632,30 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, else for (y=0; y < (ssize_t) image->rows; y++) { - register const IndexPacket - *indexes; - - register const PixelPacket + register const Quantum *p; register ssize_t x; - p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception); - if (p == (const PixelPacket *) NULL) + p=GetVirtualPixels(image,0,y,image->columns,1,exception); + if (p == (const Quantum *) NULL) break; q=jpeg_pixels; - indexes=GetVirtualIndexQueue(image); for (x=0; x < (ssize_t) image->columns; x++) { /* Convert DirectClass packets to contiguous CMYK scanlines. */ *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange- - GetRedPixelComponent(p)))); + GetPixelRed(image,p)))); *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange- - GetGreenPixelComponent(p)))); + GetPixelGreen(image,p)))); *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange- - GetBluePixelComponent(p)))); + GetPixelBlue(image,p)))); *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange- - GetIndexPixelComponent(indexes+x)))); - p++; + GetPixelBlack(image,p)))); + p+=GetPixelChannels(image); } (void) jpeg_write_scanlines(&jpeg_info,scanline,1); status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, @@ -2267,21 +2668,20 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, if (jpeg_info.in_color_space == JCS_GRAYSCALE) for (y=0; y < (ssize_t) image->rows; y++) { - register const PixelPacket + register const Quantum *p; register ssize_t x; - p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception); - if (p == (const PixelPacket *) NULL) + p=GetVirtualPixels(image,0,y,image->columns,1,exception); + if (p == (const Quantum *) NULL) break; q=jpeg_pixels; for (x=0; x < (ssize_t) image->columns; x++) { - *q++=(JSAMPLE) (ScaleQuantumToShort(PixelIntensityToQuantum(p)) >> - 4); - p++; + *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >> 4); + p+=GetPixelChannels(image); } (void) jpeg_write_scanlines(&jpeg_info,scanline,1); status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, @@ -2294,25 +2694,22 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, (jpeg_info.in_color_space == JCS_YCbCr)) for (y=0; y < (ssize_t) image->rows; y++) { - register const PixelPacket + register const Quantum *p; register ssize_t x; - p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception); - if (p == (const PixelPacket *) NULL) + p=GetVirtualPixels(image,0,y,image->columns,1,exception); + if (p == (const Quantum *) NULL) break; q=jpeg_pixels; for (x=0; x < (ssize_t) image->columns; x++) { - *q++=(JSAMPLE) (ScaleQuantumToShort(GetRedPixelComponent(p)) >> - 4); - *q++=(JSAMPLE) (ScaleQuantumToShort(GetGreenPixelComponent(p)) >> - 4); - *q++=(JSAMPLE) (ScaleQuantumToShort(GetBluePixelComponent(p)) >> - 4); - p++; + *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4); + *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4); + *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4); + p+=GetPixelChannels(image); } (void) jpeg_write_scanlines(&jpeg_info,scanline,1); status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, @@ -2323,34 +2720,30 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, else for (y=0; y < (ssize_t) image->rows; y++) { - register const IndexPacket - *indexes; - - register const PixelPacket + register const Quantum *p; register ssize_t x; - p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception); - if (p == (const PixelPacket *) NULL) + p=GetVirtualPixels(image,0,y,image->columns,1,exception); + if (p == (const Quantum *) NULL) break; q=jpeg_pixels; - indexes=GetVirtualIndexQueue(image); for (x=0; x < (ssize_t) image->columns; x++) { /* Convert DirectClass packets to contiguous CMYK scanlines. */ *q++=(JSAMPLE) (4095-(ScaleQuantumToShort( - GetRedPixelComponent(p)) >> 4)); + GetPixelRed(image,p)) >> 4)); *q++=(JSAMPLE) (4095-(ScaleQuantumToShort( - GetGreenPixelComponent(p)) >> 4)); + GetPixelGreen(image,p)) >> 4)); *q++=(JSAMPLE) (4095-(ScaleQuantumToShort( - GetBluePixelComponent(p)) >> 4)); + GetPixelBlue(image,p)) >> 4)); *q++=(JSAMPLE) (4095-(ScaleQuantumToShort( - GetIndexPixelComponent(indexes+x)) >> 4)); - p++; + GetPixelBlack(image,p)) >> 4)); + p+=GetPixelChannels(image); } (void) jpeg_write_scanlines(&jpeg_info,scanline,1); status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, @@ -2364,7 +2757,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, Relinquish resources. */ jpeg_destroy_compress(&jpeg_info); - jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); + jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels); (void) CloseBlob(image); return(MagickTrue); }