% 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 %
#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 <setjmp.h>
#if defined(MAGICKCORE_JPEG_DELEGATE)
#define JPEG_INTERNAL_OPTIONS
start_of_blob;
} SourceManager;
#endif
+
+typedef struct _QuantizationTable
+{
+ char
+ *slot,
+ *description;
+
+ size_t
+ width,
+ height;
+
+ double
+ divisor;
+
+ unsigned int
+ *levels;
+} QuantizationTable;
\f
/*
Forward declarations.
static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
{
+#define JPEGExcessiveWarnings 1000
+
char
message[JMSG_LENGTH_MAX];
Process warning message.
*/
(jpeg_info->err->format_message)(jpeg_info,message);
+ if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
+ JPEGErrorHandler(jpeg_info);
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 ((image->debug != MagickFalse) &&
previous_profile=GetImageProfile(image,name);
if (previous_profile != (const StringInfo *) NULL)
{
- ssize_t
+ size_t
length;
length=GetStringInfoLength(profile);
static void TerminateSource(j_decompress_ptr cinfo)
{
- cinfo=cinfo;
+ (void) cinfo;
}
static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
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)
option=GetImageOption(image_info,"jpeg:colors");
if (option != (const char *) NULL)
{
- /* Let the JPEG library quantize the image */
+ /*
+ Let the JPEG library quantize the image.
+ */
jpeg_info.quantize_colors=MagickTrue;
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)
}
}
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;
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].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].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;
}
}
pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
index=ConstrainColormapIndex(image,pixel,exception);
SetPixelIndex(image,index,q);
- SetPixelPixelInfo(image,image->colormap+(ssize_t) index,q);
+ SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
p++;
q+=GetPixelChannels(image);
}
{
index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
SetPixelIndex(image,index,q);
- SetPixelPixelInfo(image,image->colormap+(ssize_t) index,q);
+ SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
p++;
q+=GetPixelChannels(image);
}
%
*/
+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
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", "<description>, 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", "<levels>, 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", "<levels width>, 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", "<levels width>, 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", "<levels height>, 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", "<levels height>, 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", "<levels divisor>, 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", "<levels divisor>, 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", "<levels>, 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", "<level> 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
ErrorManager
error_manager;
+ int
+ quality;
+
JSAMPLE
*jpeg_pixels;
default:
{
if (IsRGBColorspace(image->colorspace) == MagickFalse)
- (void) TransformImageColorspace(image,RGBColorspace,exception);
+ (void) TransformImageColorspace(image,sRGBColorspace,exception);
break;
}
}
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)
}
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
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)
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;
}
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);
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
}
#endif
}
+ jpeg_set_quality(&jpeg_info,quality,MagickTrue);
sampling_factor=(const char *) NULL;
value=GetImageProperty(image,"jpeg:sampling-factor",exception);
if (value != (char *) NULL)
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)
{
q=jpeg_pixels;
for (x=0; x < (ssize_t) image->columns; x++)
{
- *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >>
- 4);
+ *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >> 4);
p+=GetPixelChannels(image);
}
(void) jpeg_write_scanlines(&jpeg_info,scanline,1);