2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % JJJJJ PPPP EEEEE GGGG %
13 % Read/Write JPEG Image Format %
20 % Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 % This software is based in part on the work of the Independent JPEG Group.
37 % See ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz for copyright and
38 % licensing restrictions. Blob support contributed by Glenn Randers-Pehrson.
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/color.h"
53 #include "MagickCore/colormap-private.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colorspace.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/exception.h"
60 #include "MagickCore/exception-private.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/magick.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/module.h"
69 #include "MagickCore/monitor.h"
70 #include "MagickCore/monitor-private.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/pixel-accessor.h"
73 #include "MagickCore/profile.h"
74 #include "MagickCore/property.h"
75 #include "MagickCore/quantum-private.h"
76 #include "MagickCore/resource_.h"
77 #include "MagickCore/splay-tree.h"
78 #include "MagickCore/static.h"
79 #include "MagickCore/string_.h"
80 #include "MagickCore/string-private.h"
81 #include "MagickCore/token.h"
82 #include "MagickCore/utility.h"
83 #include "MagickCore/xml-tree.h"
84 #include "MagickCore/xml-tree-private.h"
86 #if defined(MAGICKCORE_JPEG_DELEGATE)
87 #define JPEG_INTERNAL_OPTIONS
88 #if defined(__MINGW32__) || defined(__MINGW64__)
89 # define XMD_H 1 /* Avoid conflicting typedef for INT32 */
99 #define ICC_MARKER (JPEG_APP0+2)
100 #define ICC_PROFILE "ICC_PROFILE"
101 #define IPTC_MARKER (JPEG_APP0+13)
102 #define XML_MARKER (JPEG_APP0+1)
103 #define MaxBufferExtent 16384
106 Typedef declarations.
108 #if defined(MAGICKCORE_JPEG_DELEGATE)
109 typedef struct _DestinationManager
111 struct jpeg_destination_mgr
119 } DestinationManager;
121 typedef struct _ErrorManager
139 typedef struct _SourceManager
141 struct jpeg_source_mgr
155 typedef struct _QuantizationTable
173 Forward declarations.
175 #if defined(MAGICKCORE_JPEG_DELEGATE)
176 static MagickBooleanType
177 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191 % IsJPEG() returns MagickTrue if the image format type, identified by the
192 % magick string, is JPEG.
194 % The format of the IsJPEG method is:
196 % MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
198 % A description of each parameter follows:
200 % o magick: compare image format pattern against these bytes.
202 % o length: Specifies the length of the magick string.
205 static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
209 if (memcmp(magick,"\377\330\377",3) == 0)
214 #if defined(MAGICKCORE_JPEG_DELEGATE)
216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220 % R e a d J P E G I m a g e %
224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226 % ReadJPEGImage() reads a JPEG image file and returns it. It allocates
227 % the memory necessary for the new Image structure and returns a pointer to
230 % The format of the ReadJPEGImage method is:
232 % Image *ReadJPEGImage(const ImageInfo *image_info,
233 % ExceptionInfo *exception)
235 % A description of each parameter follows:
237 % o image_info: the image info.
239 % o exception: return any errors or warnings in this structure.
243 static boolean FillInputBuffer(j_decompress_ptr cinfo)
248 source=(SourceManager *) cinfo->src;
249 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
250 MaxBufferExtent,source->buffer);
251 if (source->manager.bytes_in_buffer == 0)
253 if (source->start_of_blob != FALSE)
254 ERREXIT(cinfo,JERR_INPUT_EMPTY);
255 WARNMS(cinfo,JWRN_JPEG_EOF);
256 source->buffer[0]=(JOCTET) 0xff;
257 source->buffer[1]=(JOCTET) JPEG_EOI;
258 source->manager.bytes_in_buffer=2;
260 source->manager.next_input_byte=source->buffer;
261 source->start_of_blob=FALSE;
265 static int GetCharacter(j_decompress_ptr jpeg_info)
267 if (jpeg_info->src->bytes_in_buffer == 0)
268 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
269 jpeg_info->src->bytes_in_buffer--;
270 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
273 static void InitializeSource(j_decompress_ptr cinfo)
278 source=(SourceManager *) cinfo->src;
279 source->start_of_blob=TRUE;
282 static MagickBooleanType IsITUFaxImage(const Image *image)
290 profile=GetImageProfile(image,"8bim");
291 if (profile == (const StringInfo *) NULL)
293 if (GetStringInfoLength(profile) < 5)
295 datum=GetStringInfoDatum(profile);
296 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
297 (datum[3] == 0x41) && (datum[4] == 0x58))
302 static void JPEGErrorHandler(j_common_ptr jpeg_info)
305 message[JMSG_LENGTH_MAX];
317 error_manager=(ErrorManager *) jpeg_info->client_data;
318 image=error_manager->image;
319 exception=error_manager->exception;
320 (jpeg_info->err->format_message)(jpeg_info,message);
321 if (image->debug != MagickFalse)
322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
323 "[%s] JPEG Trace: \"%s\"",image->filename,message);
324 if (error_manager->finished != MagickFalse)
325 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
326 (char *) message,"`%s'",image->filename);
328 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
329 (char *) message,"`%s'",image->filename);
330 longjmp(error_manager->error_recovery,1);
333 static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
335 #define JPEGExcessiveWarnings 1000
338 message[JMSG_LENGTH_MAX];
350 error_manager=(ErrorManager *) jpeg_info->client_data;
351 exception=error_manager->exception;
352 image=error_manager->image;
356 Process warning message.
358 (jpeg_info->err->format_message)(jpeg_info,message);
359 if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
360 JPEGErrorHandler(jpeg_info);
361 ThrowBinaryException(CorruptImageWarning,(char *) message,
365 if ((image->debug != MagickFalse) &&
366 (level >= jpeg_info->err->trace_level))
369 Process trace message.
371 (jpeg_info->err->format_message)(jpeg_info,message);
372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
373 "[%s] JPEG Trace: \"%s\"",image->filename,message);
378 static boolean ReadComment(j_decompress_ptr jpeg_info)
389 register unsigned char
402 Determine length of comment.
404 error_manager=(ErrorManager *) jpeg_info->client_data;
405 exception=error_manager->exception;
406 image=error_manager->image;
407 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
408 length+=GetCharacter(jpeg_info);
412 comment=BlobToStringInfo((const void *) NULL,length);
413 if (comment == (StringInfo *) NULL)
414 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
419 error_manager->profile=comment;
420 p=GetStringInfoDatum(comment);
421 for (i=0; i < (ssize_t) GetStringInfoLength(comment); i++)
422 *p++=(unsigned char) GetCharacter(jpeg_info);
424 error_manager->profile=NULL;
425 p=GetStringInfoDatum(comment);
426 (void) SetImageProperty(image,"comment",(const char *) p,exception);
427 comment=DestroyStringInfo(comment);
431 static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
451 register unsigned char
464 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
465 length+=(size_t) GetCharacter(jpeg_info);
470 (void) GetCharacter(jpeg_info);
473 for (i=0; i < 12; i++)
474 magick[i]=(char) GetCharacter(jpeg_info);
475 if (LocaleCompare(magick,ICC_PROFILE) != 0)
478 Not a ICC profile, return.
480 for (i=0; i < (ssize_t) (length-12); i++)
481 (void) GetCharacter(jpeg_info);
484 (void) GetCharacter(jpeg_info); /* id */
485 (void) GetCharacter(jpeg_info); /* markers */
487 error_manager=(ErrorManager *) jpeg_info->client_data;
488 exception=error_manager->exception;
489 image=error_manager->image;
490 profile=BlobToStringInfo((const void *) NULL,length);
491 if (profile == (StringInfo *) NULL)
492 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
494 error_manager->profile=profile;
495 p=GetStringInfoDatum(profile);
496 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
497 *p++=(unsigned char) GetCharacter(jpeg_info);
498 error_manager->profile=NULL;
499 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
500 if (icc_profile != (StringInfo *) NULL)
502 ConcatenateStringInfo(icc_profile,profile);
503 profile=DestroyStringInfo(profile);
507 status=SetImageProfile(image,"icc",profile,exception);
508 profile=DestroyStringInfo(profile);
509 if (status == MagickFalse)
510 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
513 if (image->debug != MagickFalse)
514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
515 "Profile: ICC, %.20g bytes",(double) length);
519 static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
522 magick[MaxTextExtent];
539 register unsigned char
550 Determine length of binary data stored here.
552 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
553 length+=(size_t) GetCharacter(jpeg_info);
558 (void) GetCharacter(jpeg_info);
562 Validate that this was written as a Photoshop resource format slug.
564 for (i=0; i < 10; i++)
565 magick[i]=(char) GetCharacter(jpeg_info);
570 if (LocaleCompare(magick,"Photoshop ") != 0)
573 Not a IPTC profile, return.
575 for (i=0; i < (ssize_t) length; i++)
576 (void) GetCharacter(jpeg_info);
580 Remove the version number.
582 for (i=0; i < 4; i++)
583 (void) GetCharacter(jpeg_info);
589 error_manager=(ErrorManager *) jpeg_info->client_data;
590 exception=error_manager->exception;
591 image=error_manager->image;
592 profile=BlobToStringInfo((const void *) NULL,length);
593 if (profile == (StringInfo *) NULL)
594 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
596 error_manager->profile=profile;
597 p=GetStringInfoDatum(profile);
598 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
599 *p++=(unsigned char) GetCharacter(jpeg_info);
600 error_manager->profile=NULL;
601 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
602 if (iptc_profile != (StringInfo *) NULL)
604 ConcatenateStringInfo(iptc_profile,profile);
605 profile=DestroyStringInfo(profile);
609 status=SetImageProfile(image,"8bim",profile,exception);
610 profile=DestroyStringInfo(profile);
611 if (status == MagickFalse)
612 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
615 if (image->debug != MagickFalse)
616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
617 "Profile: iptc, %.20g bytes",(double) length);
621 static boolean ReadProfile(j_decompress_ptr jpeg_info)
647 register unsigned char
657 Read generic profile.
659 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
660 length+=(size_t) GetCharacter(jpeg_info);
664 marker=jpeg_info->unread_marker-JPEG_APP0;
665 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
666 error_manager=(ErrorManager *) jpeg_info->client_data;
667 exception=error_manager->exception;
668 image=error_manager->image;
669 profile=BlobToStringInfo((const void *) NULL,length);
670 if (profile == (StringInfo *) NULL)
671 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
673 error_manager->profile=profile;
674 p=GetStringInfoDatum(profile);
675 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
676 *p++=(unsigned char) GetCharacter(jpeg_info);
677 error_manager->profile=NULL;
680 p=GetStringInfoDatum(profile);
681 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
682 (void) CopyMagickString(name,"exif",MaxTextExtent);
683 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
689 Extract namespace from XMP profile.
691 p=GetStringInfoDatum(profile);
692 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
698 if (j < (ssize_t) GetStringInfoLength(profile))
699 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
700 (void) CopyMagickString(name,"xmp",MaxTextExtent);
703 previous_profile=GetImageProfile(image,name);
704 if (previous_profile != (const StringInfo *) NULL)
709 length=GetStringInfoLength(profile);
710 SetStringInfoLength(profile,GetStringInfoLength(profile)+
711 GetStringInfoLength(previous_profile));
712 (void) memmove(GetStringInfoDatum(profile)+
713 GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
715 (void) memcpy(GetStringInfoDatum(profile),
716 GetStringInfoDatum(previous_profile),
717 GetStringInfoLength(previous_profile));
719 status=SetImageProfile(image,name,profile,exception);
720 profile=DestroyStringInfo(profile);
721 if (status == MagickFalse)
722 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
724 if (image->debug != MagickFalse)
725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
726 "Profile: %s, %.20g bytes",name,(double) length);
730 static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
735 if (number_bytes <= 0)
737 source=(SourceManager *) cinfo->src;
738 while (number_bytes > (long) source->manager.bytes_in_buffer)
740 number_bytes-=(long) source->manager.bytes_in_buffer;
741 (void) FillInputBuffer(cinfo);
743 source->manager.next_input_byte+=number_bytes;
744 source->manager.bytes_in_buffer-=number_bytes;
747 static void TerminateSource(j_decompress_ptr cinfo)
752 static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
757 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
758 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
759 source=(SourceManager *) cinfo->src;
760 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
761 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
762 source=(SourceManager *) cinfo->src;
763 source->manager.init_source=InitializeSource;
764 source->manager.fill_input_buffer=FillInputBuffer;
765 source->manager.skip_input_data=SkipInputData;
766 source->manager.resync_to_restart=jpeg_resync_to_restart;
767 source->manager.term_source=TerminateSource;
768 source->manager.bytes_in_buffer=0;
769 source->manager.next_input_byte=NULL;
773 static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
776 image->quality=UndefinedCompressionQuality;
777 #if defined(D_PROGRESSIVE_SUPPORTED)
778 if (image->compression == LosslessJPEGCompression)
781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
782 "Quality: 100 (lossless)");
796 Determine the JPEG compression quality from the quantization tables.
799 for (i=0; i < NUM_QUANT_TBLS; i++)
801 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
802 for (j=0; j < DCTSIZE2; j++)
803 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
805 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
806 (jpeg_info->quant_tbl_ptrs[1] != NULL))
811 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
812 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
813 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
814 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
815 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
816 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
817 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
818 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
819 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
820 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
825 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
826 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
827 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
828 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
829 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
830 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
831 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
832 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
833 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
834 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
835 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
839 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
840 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
841 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
842 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
843 for (i=0; i < 100; i++)
845 if ((qvalue < hash[i]) && (sum < sums[i]))
847 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
848 image->quality=(size_t) i+1;
849 if (image->debug != MagickFalse)
850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
851 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
852 (sum <= sums[i]) ? "exact" : "approximate");
857 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
862 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
863 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
864 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
865 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
866 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
867 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
868 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
869 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
870 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
871 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
876 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
877 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
878 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
879 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
880 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
881 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
882 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
883 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
884 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
885 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
886 667, 592, 518, 441, 369, 292, 221, 151, 86,
890 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
891 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
892 for (i=0; i < 100; i++)
894 if ((qvalue < hash[i]) && (sum < sums[i]))
896 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
897 image->quality=(size_t) i+1;
898 if (image->debug != MagickFalse)
899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
900 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
901 (sum <= sums[i]) ? "exact" : "approximate");
908 static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
911 sampling_factor[MaxTextExtent];
913 switch (jpeg_info->out_color_space)
917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
918 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
919 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
920 jpeg_info->comp_info[0].v_samp_factor,
921 jpeg_info->comp_info[1].h_samp_factor,
922 jpeg_info->comp_info[1].v_samp_factor,
923 jpeg_info->comp_info[2].h_samp_factor,
924 jpeg_info->comp_info[2].v_samp_factor,
925 jpeg_info->comp_info[3].h_samp_factor,
926 jpeg_info->comp_info[3].v_samp_factor);
931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
932 "Colorspace: GRAYSCALE");
933 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
934 jpeg_info->comp_info[0].h_samp_factor,
935 jpeg_info->comp_info[0].v_samp_factor);
940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
941 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
942 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
943 jpeg_info->comp_info[0].v_samp_factor,
944 jpeg_info->comp_info[1].h_samp_factor,
945 jpeg_info->comp_info[1].v_samp_factor,
946 jpeg_info->comp_info[2].h_samp_factor,
947 jpeg_info->comp_info[2].v_samp_factor);
952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
953 jpeg_info->out_color_space);
954 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
955 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
956 jpeg_info->comp_info[0].v_samp_factor,
957 jpeg_info->comp_info[1].h_samp_factor,
958 jpeg_info->comp_info[1].v_samp_factor,
959 jpeg_info->comp_info[2].h_samp_factor,
960 jpeg_info->comp_info[2].v_samp_factor,
961 jpeg_info->comp_info[3].h_samp_factor,
962 jpeg_info->comp_info[3].v_samp_factor);
966 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
972 static Image *ReadJPEGImage(const ImageInfo *image_info,
973 ExceptionInfo *exception)
976 value[MaxTextExtent];
988 *volatile jpeg_pixels;
1006 struct jpeg_decompress_struct
1009 struct jpeg_error_mgr
1025 assert(image_info != (const ImageInfo *) NULL);
1026 assert(image_info->signature == MagickSignature);
1027 if (image_info->debug != MagickFalse)
1028 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1029 image_info->filename);
1030 assert(exception != (ExceptionInfo *) NULL);
1031 assert(exception->signature == MagickSignature);
1032 debug=IsEventLogging();
1034 image=AcquireImage(image_info,exception);
1035 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1036 if (status == MagickFalse)
1038 image=DestroyImageList(image);
1039 return((Image *) NULL);
1042 Initialize JPEG parameters.
1044 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
1045 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1046 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1047 jpeg_info.err=jpeg_std_error(&jpeg_error);
1048 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
1049 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
1050 jpeg_pixels=(JSAMPLE *) NULL;
1051 error_manager.exception=exception;
1052 error_manager.image=image;
1053 if (setjmp(error_manager.error_recovery) != 0)
1055 jpeg_destroy_decompress(&jpeg_info);
1056 if (error_manager.profile != (StringInfo *) NULL)
1057 error_manager.profile=DestroyStringInfo(error_manager.profile);
1058 (void) CloseBlob(image);
1059 number_pixels=(MagickSizeType) image->columns*image->rows;
1060 if (number_pixels != 0)
1061 return(GetFirstImageInList(image));
1062 return(DestroyImage(image));
1064 jpeg_info.client_data=(void *) &error_manager;
1065 jpeg_create_decompress(&jpeg_info);
1066 JPEGSourceManager(&jpeg_info,image);
1067 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
1068 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
1069 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1070 for (i=1; i < 16; i++)
1071 if ((i != 2) && (i != 13) && (i != 14))
1072 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
1073 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
1074 if ((image_info->colorspace == YCbCrColorspace) ||
1075 (image_info->colorspace == Rec601YCbCrColorspace) ||
1076 (image_info->colorspace == Rec709YCbCrColorspace))
1077 jpeg_info.out_color_space=JCS_YCbCr;
1079 Set image resolution.
1082 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1083 (jpeg_info.Y_density != 1))
1085 image->resolution.x=(double) jpeg_info.X_density;
1086 image->resolution.y=(double) jpeg_info.Y_density;
1087 units=(size_t) jpeg_info.density_unit;
1090 image->units=PixelsPerInchResolution;
1092 image->units=PixelsPerCentimeterResolution;
1093 number_pixels=(MagickSizeType) image->columns*image->rows;
1094 option=GetImageArtifact(image,"jpeg:size");
1095 if (option != (const char *) NULL)
1109 flags=ParseGeometry(option,&geometry_info);
1110 if ((flags & SigmaValue) == 0)
1111 geometry_info.sigma=geometry_info.rho;
1112 jpeg_calc_output_dimensions(&jpeg_info);
1113 image->magick_columns=jpeg_info.output_width;
1114 image->magick_rows=jpeg_info.output_height;
1116 if (geometry_info.rho != 0.0)
1117 scale_factor=jpeg_info.output_width/geometry_info.rho;
1118 if ((geometry_info.sigma != 0.0) &&
1119 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1120 scale_factor=jpeg_info.output_height/geometry_info.sigma;
1121 jpeg_info.scale_num=1U;
1122 jpeg_info.scale_denom=(unsigned int) scale_factor;
1123 jpeg_calc_output_dimensions(&jpeg_info);
1124 if (image->debug != MagickFalse)
1125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1126 "Scale factor: %.20g",(double) scale_factor);
1128 precision=(size_t) jpeg_info.data_precision;
1129 #if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1130 #if defined(D_LOSSLESS_SUPPORTED)
1131 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1132 JPEGInterlace : NoInterlace;
1133 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1134 LosslessJPEGCompression : JPEGCompression;
1135 if (jpeg_info.data_precision > 8)
1136 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1137 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1139 if (jpeg_info.data_precision == 16)
1140 jpeg_info.data_precision=12;
1142 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1144 image->compression=JPEGCompression;
1147 image->compression=JPEGCompression;
1148 image->interlace=JPEGInterlace;
1150 option=GetImageArtifact(image,"jpeg:colors");
1151 if (option != (const char *) NULL)
1154 Let the JPEG library quantize the image.
1156 jpeg_info.quantize_colors=MagickTrue;
1157 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
1159 option=GetImageArtifact(image,"jpeg:block-smoothing");
1160 if (option != (const char *) NULL)
1161 jpeg_info.do_block_smoothing=IsStringTrue(option);
1162 jpeg_info.dct_method=JDCT_FLOAT;
1163 option=GetImageArtifact(image,"jpeg:dct-method");
1164 if (option != (const char *) NULL)
1170 if (LocaleCompare(option,"default") == 0)
1171 jpeg_info.dct_method=JDCT_DEFAULT;
1177 if (LocaleCompare(option,"fastest") == 0)
1178 jpeg_info.dct_method=JDCT_FASTEST;
1179 if (LocaleCompare(option,"float") == 0)
1180 jpeg_info.dct_method=JDCT_FLOAT;
1186 if (LocaleCompare(option,"ifast") == 0)
1187 jpeg_info.dct_method=JDCT_IFAST;
1188 if (LocaleCompare(option,"islow") == 0)
1189 jpeg_info.dct_method=JDCT_ISLOW;
1193 option=GetImageArtifact(image,"jpeg:fancy-upsampling");
1194 if (option != (const char *) NULL)
1195 jpeg_info.do_fancy_upsampling=IsStringTrue(option);
1196 (void) jpeg_start_decompress(&jpeg_info);
1197 image->columns=jpeg_info.output_width;
1198 image->rows=jpeg_info.output_height;
1199 image->depth=(size_t) jpeg_info.data_precision;
1200 switch (jpeg_info.out_color_space)
1205 (void) SetImageColorspace(image,sRGBColorspace,exception);
1210 (void) SetImageColorspace(image,GRAYColorspace,exception);
1215 (void) SetImageColorspace(image,YCbCrColorspace,exception);
1220 (void) SetImageColorspace(image,CMYKColorspace,exception);
1224 if (IsITUFaxImage(image) != MagickFalse)
1226 (void) SetImageColorspace(image,LabColorspace,exception);
1227 jpeg_info.out_color_space=JCS_YCbCr;
1229 option=GetImageArtifact(image,"jpeg:colors");
1230 if (option != (const char *) NULL)
1231 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1233 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1234 if ((jpeg_info.output_components == 1) &&
1235 (jpeg_info.quantize_colors == MagickFalse))
1240 colors=(size_t) GetQuantumRange(image->depth)+1;
1241 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
1242 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1244 if (image->debug != MagickFalse)
1246 if (image->interlace != NoInterlace)
1247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1248 "Interlace: progressive");
1250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1251 "Interlace: nonprogressive");
1252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1253 (int) jpeg_info.data_precision);
1254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1255 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1257 JPEGSetImageQuality(&jpeg_info,image);
1258 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
1259 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
1260 jpeg_info.out_color_space);
1261 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
1262 if (image_info->ping != MagickFalse)
1264 jpeg_destroy_decompress(&jpeg_info);
1265 (void) CloseBlob(image);
1266 return(GetFirstImageInList(image));
1268 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1269 jpeg_info.output_components*sizeof(JSAMPLE));
1270 if (jpeg_pixels == (JSAMPLE *) NULL)
1271 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1273 Convert JPEG pixels to pixel packets.
1275 if (setjmp(error_manager.error_recovery) != 0)
1277 if (jpeg_pixels != (JSAMPLE *) NULL)
1278 jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
1279 jpeg_destroy_decompress(&jpeg_info);
1280 (void) CloseBlob(image);
1281 number_pixels=(MagickSizeType) image->columns*image->rows;
1282 if (number_pixels != 0)
1283 return(GetFirstImageInList(image));
1284 return(DestroyImage(image));
1286 if (jpeg_info.quantize_colors != MagickFalse)
1288 image->colors=(size_t) jpeg_info.actual_number_of_colors;
1289 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
1290 for (i=0; i < (ssize_t) image->colors; i++)
1292 image->colormap[i].red=(double) ScaleCharToQuantum(
1293 jpeg_info.colormap[0][i]);
1294 image->colormap[i].green=image->colormap[i].red;
1295 image->colormap[i].blue=image->colormap[i].red;
1296 image->colormap[i].alpha=OpaqueAlpha;
1299 for (i=0; i < (ssize_t) image->colors; i++)
1301 image->colormap[i].red=(double) ScaleCharToQuantum(
1302 jpeg_info.colormap[0][i]);
1303 image->colormap[i].green=(double) ScaleCharToQuantum(
1304 jpeg_info.colormap[1][i]);
1305 image->colormap[i].blue=(double) ScaleCharToQuantum(
1306 jpeg_info.colormap[2][i]);
1307 image->colormap[i].alpha=OpaqueAlpha;
1310 scanline[0]=(JSAMPROW) jpeg_pixels;
1311 for (y=0; y < (ssize_t) image->rows; y++)
1319 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1321 (void) ThrowMagickException(exception,GetMagickModule(),
1322 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1326 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1327 if (q == (Quantum *) NULL)
1329 if (jpeg_info.data_precision > 8)
1331 if (jpeg_info.output_components == 1)
1332 for (x=0; x < (ssize_t) image->columns; x++)
1337 if (precision != 16)
1338 pixel=(size_t) GETJSAMPLE(*p);
1340 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
1341 index=ConstrainColormapIndex(image,pixel,exception);
1342 SetPixelIndex(image,index,q);
1343 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
1345 q+=GetPixelChannels(image);
1348 if (image->colorspace != CMYKColorspace)
1349 for (x=0; x < (ssize_t) image->columns; x++)
1351 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1352 (GETJSAMPLE(*p++) << 4)),q);
1353 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1354 (GETJSAMPLE(*p++) << 4)),q);
1355 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1356 (GETJSAMPLE(*p++) << 4)),q);
1357 SetPixelAlpha(image,OpaqueAlpha,q);
1358 q+=GetPixelChannels(image);
1361 for (x=0; x < (ssize_t) image->columns; x++)
1363 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1364 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1365 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1366 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1367 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1368 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1369 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1370 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1371 SetPixelAlpha(image,OpaqueAlpha,q);
1372 q+=GetPixelChannels(image);
1376 if (jpeg_info.output_components == 1)
1377 for (x=0; x < (ssize_t) image->columns; x++)
1379 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
1380 SetPixelIndex(image,index,q);
1381 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
1383 q+=GetPixelChannels(image);
1386 if (image->colorspace != CMYKColorspace)
1387 for (x=0; x < (ssize_t) image->columns; x++)
1389 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1390 GETJSAMPLE(*p++)),q);
1391 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1392 GETJSAMPLE(*p++)),q);
1393 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1394 GETJSAMPLE(*p++)),q);
1395 SetPixelAlpha(image,OpaqueAlpha,q);
1396 q+=GetPixelChannels(image);
1399 for (x=0; x < (ssize_t) image->columns; x++)
1401 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1402 (unsigned char) GETJSAMPLE(*p++)),q);
1403 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1404 (unsigned char) GETJSAMPLE(*p++)),q);
1405 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1406 (unsigned char) GETJSAMPLE(*p++)),q);
1407 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1408 (unsigned char) GETJSAMPLE(*p++)),q);
1409 SetPixelAlpha(image,OpaqueAlpha,q);
1410 q+=GetPixelChannels(image);
1412 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1414 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1416 if (status == MagickFalse)
1418 jpeg_abort_decompress(&jpeg_info);
1422 if (status != MagickFalse)
1424 error_manager.finished=MagickTrue;
1425 if (setjmp(error_manager.error_recovery) == 0)
1426 (void) jpeg_finish_decompress(&jpeg_info);
1429 Free jpeg resources.
1431 jpeg_destroy_decompress(&jpeg_info);
1432 jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
1433 (void) CloseBlob(image);
1434 return(GetFirstImageInList(image));
1439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443 % R e g i s t e r J P E G I m a g e %
1447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449 % RegisterJPEGImage() adds properties for the JPEG image format to
1450 % the list of supported formats. The properties include the image format
1451 % tag, a method to read and/or write the format, whether the format
1452 % supports the saving of more than one frame to the same file or blob,
1453 % whether the format supports native in-memory I/O, and a brief
1454 % description of the format.
1456 % The format of the RegisterJPEGImage method is:
1458 % size_t RegisterJPEGImage(void)
1461 ModuleExport size_t RegisterJPEGImage(void)
1464 version[MaxTextExtent];
1470 description[] = "Joint Photographic Experts Group JFIF format";
1473 #if defined(JPEG_LIB_VERSION)
1474 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
1476 entry=SetMagickInfo("JPEG");
1477 entry->thread_support=NoThreadSupport;
1478 #if defined(MAGICKCORE_JPEG_DELEGATE)
1479 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1480 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1482 entry->magick=(IsImageFormatHandler *) IsJPEG;
1483 entry->adjoin=MagickFalse;
1484 entry->description=ConstantString(description);
1485 if (*version != '\0')
1486 entry->version=ConstantString(version);
1487 entry->module=ConstantString("JPEG");
1488 (void) RegisterMagickInfo(entry);
1489 entry=SetMagickInfo("JPG");
1490 entry->thread_support=NoThreadSupport;
1491 #if defined(MAGICKCORE_JPEG_DELEGATE)
1492 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1493 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1495 entry->adjoin=MagickFalse;
1496 entry->description=ConstantString(description);
1497 if (*version != '\0')
1498 entry->version=ConstantString(version);
1499 entry->module=ConstantString("JPEG");
1500 (void) RegisterMagickInfo(entry);
1501 entry=SetMagickInfo("PJPEG");
1502 entry->thread_support=NoThreadSupport;
1503 #if defined(MAGICKCORE_JPEG_DELEGATE)
1504 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1505 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1507 entry->adjoin=MagickFalse;
1508 entry->description=ConstantString(description);
1509 if (*version != '\0')
1510 entry->version=ConstantString(version);
1511 entry->module=ConstantString("JPEG");
1512 (void) RegisterMagickInfo(entry);
1513 return(MagickImageCoderSignature);
1517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521 % U n r e g i s t e r J P E G I m a g e %
1525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1527 % UnregisterJPEGImage() removes format registrations made by the
1528 % JPEG module from the list of supported formats.
1530 % The format of the UnregisterJPEGImage method is:
1532 % UnregisterJPEGImage(void)
1535 ModuleExport void UnregisterJPEGImage(void)
1537 (void) UnregisterMagickInfo("PJPG");
1538 (void) UnregisterMagickInfo("JPEG");
1539 (void) UnregisterMagickInfo("JPG");
1542 #if defined(MAGICKCORE_JPEG_DELEGATE)
1544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548 % W r i t e J P E G I m a g e %
1552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554 % WriteJPEGImage() writes a JPEG image file and returns it. It
1555 % allocates the memory necessary for the new Image structure and returns a
1556 % pointer to the new image.
1558 % The format of the WriteJPEGImage method is:
1560 % MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1561 % Image *image,ExceptionInfo *exception)
1563 % A description of each parameter follows:
1565 % o image_info: the image info.
1567 % o jpeg_image: The image.
1569 % o exception: return any errors or warnings in this structure.
1573 static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1575 assert(table != (QuantizationTable *) NULL);
1576 if (table->slot != (char *) NULL)
1577 table->slot=DestroyString(table->slot);
1578 if (table->description != (char *) NULL)
1579 table->description=DestroyString(table->description);
1580 if (table->levels != (unsigned int *) NULL)
1581 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1582 table=(QuantizationTable *) RelinquishMagickMemory(table);
1586 static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1591 destination=(DestinationManager *) cinfo->dest;
1592 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1593 MaxBufferExtent,destination->buffer);
1594 if (destination->manager.free_in_buffer != MaxBufferExtent)
1595 ERREXIT(cinfo,JERR_FILE_WRITE);
1596 destination->manager.next_output_byte=destination->buffer;
1600 static QuantizationTable *GetQuantizationTable(const char *filename,
1601 const char *slot,ExceptionInfo *exception)
1629 *quantization_tables,
1632 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1633 "Loading quantization tables \"%s\" ...",filename);
1634 table=(QuantizationTable *) NULL;
1635 xml=FileToString(filename,~0,exception);
1636 if (xml == (char *) NULL)
1638 quantization_tables=NewXMLTree(xml,exception);
1639 if (quantization_tables == (XMLTreeInfo *) NULL)
1641 xml=DestroyString(xml);
1644 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1645 table_iterator != (XMLTreeInfo *) NULL;
1646 table_iterator=GetNextXMLTreeTag(table_iterator))
1648 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1649 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1651 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1652 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1655 if (table_iterator == (XMLTreeInfo *) NULL)
1657 xml=DestroyString(xml);
1660 description=GetXMLTreeChild(table_iterator,"description");
1661 if (description == (XMLTreeInfo *) NULL)
1663 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1664 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1665 quantization_tables=DestroyXMLTree(quantization_tables);
1666 xml=DestroyString(xml);
1669 levels=GetXMLTreeChild(table_iterator,"levels");
1670 if (levels == (XMLTreeInfo *) NULL)
1672 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1673 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1674 quantization_tables=DestroyXMLTree(quantization_tables);
1675 xml=DestroyString(xml);
1678 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1679 if (table == (QuantizationTable *) NULL)
1680 ThrowFatalException(ResourceLimitFatalError,
1681 "UnableToAcquireQuantizationTable");
1682 table->slot=(char *) NULL;
1683 table->description=(char *) NULL;
1684 table->levels=(unsigned int *) NULL;
1685 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1686 if (attribute != (char *) NULL)
1687 table->slot=ConstantString(attribute);
1688 content=GetXMLTreeContent(description);
1689 if (content != (char *) NULL)
1690 table->description=ConstantString(content);
1691 attribute=GetXMLTreeAttribute(levels,"width");
1692 if (attribute == (char *) NULL)
1694 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1695 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1696 quantization_tables=DestroyXMLTree(quantization_tables);
1697 table=DestroyQuantizationTable(table);
1698 xml=DestroyString(xml);
1701 table->width=StringToUnsignedLong(attribute);
1702 if (table->width == 0)
1704 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1705 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1706 quantization_tables=DestroyXMLTree(quantization_tables);
1707 table=DestroyQuantizationTable(table);
1708 xml=DestroyString(xml);
1711 attribute=GetXMLTreeAttribute(levels,"height");
1712 if (attribute == (char *) NULL)
1714 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1715 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1716 quantization_tables=DestroyXMLTree(quantization_tables);
1717 table=DestroyQuantizationTable(table);
1718 xml=DestroyString(xml);
1721 table->height=StringToUnsignedLong(attribute);
1722 if (table->height == 0)
1724 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1725 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1726 quantization_tables=DestroyXMLTree(quantization_tables);
1727 table=DestroyQuantizationTable(table);
1728 xml=DestroyString(xml);
1731 attribute=GetXMLTreeAttribute(levels,"divisor");
1732 if (attribute == (char *) NULL)
1734 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1735 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1736 quantization_tables=DestroyXMLTree(quantization_tables);
1737 table=DestroyQuantizationTable(table);
1738 xml=DestroyString(xml);
1741 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1742 if (table->divisor == 0.0)
1744 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1745 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1746 quantization_tables=DestroyXMLTree(quantization_tables);
1747 table=DestroyQuantizationTable(table);
1748 xml=DestroyString(xml);
1751 content=GetXMLTreeContent(levels);
1752 if (content == (char *) NULL)
1754 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1755 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1756 quantization_tables=DestroyXMLTree(quantization_tables);
1757 table=DestroyQuantizationTable(table);
1758 xml=DestroyString(xml);
1761 length=(size_t) table->width*table->height;
1764 table->levels=(unsigned int *) AcquireQuantumMemory(length,
1765 sizeof(*table->levels));
1766 if (table->levels == (unsigned int *) NULL)
1767 ThrowFatalException(ResourceLimitFatalError,
1768 "UnableToAcquireQuantizationTable");
1769 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1771 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1772 table->divisor+0.5);
1773 while (isspace((int) ((unsigned char) *p)) != 0)
1779 value=InterpretLocaleValue(content,&p);
1783 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1784 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1785 quantization_tables=DestroyXMLTree(quantization_tables);
1786 table=DestroyQuantizationTable(table);
1787 xml=DestroyString(xml);
1790 for (j=i; j < 64; j++)
1791 table->levels[j]=table->levels[j-1];
1792 quantization_tables=DestroyXMLTree(quantization_tables);
1793 xml=DestroyString(xml);
1797 static void InitializeDestination(j_compress_ptr cinfo)
1802 destination=(DestinationManager *) cinfo->dest;
1803 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1804 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1805 destination->manager.next_output_byte=destination->buffer;
1806 destination->manager.free_in_buffer=MaxBufferExtent;
1809 static inline size_t MagickMin(const size_t x,const size_t y)
1816 static void TerminateDestination(j_compress_ptr cinfo)
1821 destination=(DestinationManager *) cinfo->dest;
1822 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1827 count=WriteBlob(destination->image,MaxBufferExtent-
1828 destination->manager.free_in_buffer,destination->buffer);
1829 if (count != (ssize_t)
1830 (MaxBufferExtent-destination->manager.free_in_buffer))
1831 ERREXIT(cinfo,JERR_FILE_WRITE);
1835 static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1857 Save image profile as a APP marker.
1860 custom_profile=AcquireStringInfo(65535L);
1861 ResetImageProfileIterator(image);
1862 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1864 register unsigned char
1867 profile=GetImageProfile(image,name);
1868 p=GetStringInfoDatum(custom_profile);
1869 if (LocaleCompare(name,"EXIF") == 0)
1870 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
1872 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1873 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1874 (unsigned int) length);
1876 if (LocaleCompare(name,"ICC") == 0)
1878 register unsigned char
1882 p=GetStringInfoDatum(custom_profile);
1883 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
1884 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
1886 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
1887 p[12]=(unsigned char) ((i/65519L)+1);
1888 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1889 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1891 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1892 custom_profile),(unsigned int) (length+tag_length));
1895 if (((LocaleCompare(name,"IPTC") == 0) ||
1896 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1902 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
1904 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
1905 roundup=(size_t) (length & 0x01);
1906 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1908 (void) memcpy(p,"Photoshop 3.0 ",14);
1913 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1915 p[24]=(unsigned char) (length >> 8);
1916 p[25]=(unsigned char) (length & 0xff);
1919 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
1921 p[length+tag_length]='\0';
1922 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1923 custom_profile),(unsigned int) (length+tag_length+roundup));
1926 if (LocaleCompare(name,"XMP") == 0)
1932 Add namespace to XMP profile.
1934 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
1935 ConcatenateStringInfo(xmp_profile,profile);
1936 GetStringInfoDatum(xmp_profile)[28]='\0';
1937 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
1939 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1940 jpeg_write_marker(jpeg_info,XML_MARKER,
1941 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1943 xmp_profile=DestroyStringInfo(xmp_profile);
1945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1946 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
1947 name=GetNextImageProfile(image);
1949 custom_profile=DestroyStringInfo(custom_profile);
1952 static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1957 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1958 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1959 destination=(DestinationManager *) cinfo->dest;
1960 destination->manager.init_destination=InitializeDestination;
1961 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1962 destination->manager.term_destination=TerminateDestination;
1963 destination->image=image;
1966 static char **SamplingFactorToList(const char *text)
1983 if (text == (char *) NULL)
1984 return((char **) NULL);
1986 Convert string to an ASCII list.
1989 for (p=text; *p != '\0'; p++)
1992 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1994 if (textlist == (char **) NULL)
1995 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1997 for (i=0; i < (ssize_t) lines; i++)
1999 for (q=(char *) p; *q != '\0'; q++)
2002 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
2003 sizeof(*textlist[i]));
2004 if (textlist[i] == (char *) NULL)
2005 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2006 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2011 textlist[i]=(char *) NULL;
2015 static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
2016 Image *image,ExceptionInfo *exception)
2030 *volatile jpeg_pixels;
2047 struct jpeg_compress_struct
2050 struct jpeg_error_mgr
2056 assert(image_info != (const ImageInfo *) NULL);
2057 assert(image_info->signature == MagickSignature);
2058 assert(image != (Image *) NULL);
2059 assert(image->signature == MagickSignature);
2060 if (image->debug != MagickFalse)
2061 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2062 assert(exception != (ExceptionInfo *) NULL);
2063 assert(exception->signature == MagickSignature);
2064 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2065 if (status == MagickFalse)
2068 Initialize JPEG parameters.
2070 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
2071 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2072 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2073 jpeg_info.client_data=(void *) image;
2074 jpeg_info.err=jpeg_std_error(&jpeg_error);
2075 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
2076 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
2077 error_manager.exception=exception;
2078 error_manager.image=image;
2079 jpeg_pixels=(JSAMPLE *) NULL;
2080 if (setjmp(error_manager.error_recovery) != 0)
2082 jpeg_destroy_compress(&jpeg_info);
2083 (void) CloseBlob(image);
2084 return(MagickFalse);
2086 jpeg_info.client_data=(void *) &error_manager;
2087 jpeg_create_compress(&jpeg_info);
2088 JPEGDestinationManager(&jpeg_info,image);
2089 if ((image->columns != (unsigned int) image->columns) ||
2090 (image->rows != (unsigned int) image->rows))
2091 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2092 jpeg_info.image_width=(unsigned int) image->columns;
2093 jpeg_info.image_height=(unsigned int) image->rows;
2094 jpeg_info.input_components=3;
2095 jpeg_info.data_precision=8;
2096 jpeg_info.in_color_space=JCS_RGB;
2097 switch (image->colorspace)
2099 case CMYKColorspace:
2101 jpeg_info.input_components=4;
2102 jpeg_info.in_color_space=JCS_CMYK;
2105 case YCbCrColorspace:
2106 case Rec601YCbCrColorspace:
2107 case Rec709YCbCrColorspace:
2109 jpeg_info.in_color_space=JCS_YCbCr;
2112 case GRAYColorspace:
2114 jpeg_info.input_components=1;
2115 jpeg_info.in_color_space=JCS_GRAYSCALE;
2120 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
2121 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2125 if ((image_info->type != TrueColorType) &&
2126 (IsImageGray(image,exception) != MagickFalse))
2128 jpeg_info.input_components=1;
2129 jpeg_info.in_color_space=JCS_GRAYSCALE;
2131 jpeg_set_defaults(&jpeg_info);
2132 if (jpeg_info.in_color_space == JCS_CMYK)
2133 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
2134 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2135 jpeg_info.data_precision=8;
2137 if (sizeof(JSAMPLE) > 1)
2138 jpeg_info.data_precision=12;
2139 jpeg_info.density_unit=(UINT8) 1;
2140 if (image->debug != MagickFalse)
2141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2142 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2143 floor(image->resolution.y+0.5));
2144 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
2147 Set image resolution.
2149 jpeg_info.write_JFIF_header=MagickTrue;
2150 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2151 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
2152 if (image->units == PixelsPerInchResolution)
2153 jpeg_info.density_unit=(UINT8) 1;
2154 if (image->units == PixelsPerCentimeterResolution)
2155 jpeg_info.density_unit=(UINT8) 2;
2157 jpeg_info.dct_method=JDCT_FLOAT;
2158 option=GetImageArtifact(image,"jpeg:dct-method");
2159 if (option != (const char *) NULL)
2165 if (LocaleCompare(option,"default") == 0)
2166 jpeg_info.dct_method=JDCT_DEFAULT;
2172 if (LocaleCompare(option,"fastest") == 0)
2173 jpeg_info.dct_method=JDCT_FASTEST;
2174 if (LocaleCompare(option,"float") == 0)
2175 jpeg_info.dct_method=JDCT_FLOAT;
2181 if (LocaleCompare(option,"ifast") == 0)
2182 jpeg_info.dct_method=JDCT_IFAST;
2183 if (LocaleCompare(option,"islow") == 0)
2184 jpeg_info.dct_method=JDCT_ISLOW;
2188 option=GetImageArtifact(image,"jpeg:optimize-coding");
2189 if (option != (const char *) NULL)
2190 jpeg_info.optimize_coding=IsStringTrue(option);
2196 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2197 image->rows*sizeof(JSAMPLE);
2198 if (length == (MagickSizeType) ((size_t) length))
2201 Perform optimization only if available memory resources permit it.
2203 status=AcquireMagickResource(MemoryResource,length);
2204 RelinquishMagickResource(MemoryResource,length);
2205 jpeg_info.optimize_coding=status;
2208 #if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2209 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2210 (image_info->interlace != NoInterlace))
2212 if (image->debug != MagickFalse)
2213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2214 "Interlace: progressive");
2215 jpeg_simple_progression(&jpeg_info);
2218 if (image->debug != MagickFalse)
2219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2220 "Interlace: non-progressive");
2222 if (image->debug != MagickFalse)
2223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2224 "Interlace: nonprogressive");
2226 option=GetImageArtifact(image,"jpeg:extent");
2227 if (option != (const char *) NULL)
2235 jpeg_info=CloneImageInfo(image_info);
2236 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
2237 if (jpeg_image != (Image *) NULL)
2247 Search for compression quality that does not exceed image extent.
2249 jpeg_info->quality=0;
2250 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
2251 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
2252 (void) AcquireUniqueFilename(jpeg_image->filename);
2254 for (minimum=2; minimum != maximum; )
2256 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
2257 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
2258 if (GetBlobSize(jpeg_image) <= extent)
2259 minimum=jpeg_image->quality+1;
2261 maximum=jpeg_image->quality-1;
2263 (void) RelinquishUniqueFileResource(jpeg_image->filename);
2264 image->quality=minimum-1;
2265 jpeg_image=DestroyImage(jpeg_image);
2267 jpeg_info=DestroyImageInfo(jpeg_info);
2270 if ((image_info->compression != LosslessJPEGCompression) &&
2271 (image->quality <= 100))
2273 if (image->quality != UndefinedCompressionQuality)
2274 quality=(int) image->quality;
2275 if (image->debug != MagickFalse)
2276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2277 (double) image->quality);
2281 #if !defined(C_LOSSLESS_SUPPORTED)
2283 if (image->debug != MagickFalse)
2284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2286 if (image->quality < 100)
2287 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2288 "LosslessToLossyJPEGConversion",image->filename);
2295 predictor=image->quality/100; /* range 1-7 */
2296 point_transform=image->quality % 20; /* range 0-15 */
2297 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2298 if (image->debug != MagickFalse)
2300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2301 "Compression: lossless");
2302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2303 "Predictor: %d",predictor);
2304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2305 "Point Transform: %d",point_transform);
2310 jpeg_set_quality(&jpeg_info,quality,MagickTrue);
2311 #if (JPEG_LIB_VERSION >= 70)
2312 option=GetImageArtifact(image,"quality");
2313 if (option != (const char *) NULL)
2322 Set quality scaling for luminance and chrominance separately.
2324 flags=ParseGeometry(option,&geometry_info);
2325 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2327 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2328 (geometry_info.rho+0.5));
2329 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2330 (geometry_info.sigma+0.5));
2331 jpeg_default_qtables(&jpeg_info,MagickTrue);
2335 sampling_factor=(const char *) NULL;
2336 value=GetImageArtifact(image,"jpeg:sampling-factor");
2337 if (value == (char *) NULL)
2338 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2339 if (value != (char *) NULL)
2341 sampling_factor=value;
2342 if (image->debug != MagickFalse)
2343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2344 " Input sampling-factors=%s",sampling_factor);
2346 if (image_info->sampling_factor != (char *) NULL)
2347 sampling_factor=image_info->sampling_factor;
2348 if (sampling_factor == (const char *) NULL)
2350 if (image->quality >= 90)
2351 for (i=0; i < MAX_COMPONENTS; i++)
2353 jpeg_info.comp_info[i].h_samp_factor=1;
2354 jpeg_info.comp_info[i].v_samp_factor=1;
2369 Set sampling factor.
2372 factors=SamplingFactorToList(sampling_factor);
2373 if (factors != (char **) NULL)
2375 for (i=0; i < MAX_COMPONENTS; i++)
2377 if (factors[i] == (char *) NULL)
2379 flags=ParseGeometry(factors[i],&geometry_info);
2380 if ((flags & SigmaValue) == 0)
2381 geometry_info.sigma=geometry_info.rho;
2382 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2383 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2384 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2386 factors=(char **) RelinquishMagickMemory(factors);
2388 for ( ; i < MAX_COMPONENTS; i++)
2390 jpeg_info.comp_info[i].h_samp_factor=1;
2391 jpeg_info.comp_info[i].v_samp_factor=1;
2394 if (jpeg_info.input_components == 1)
2395 for (i=0; i < MAX_COMPONENTS; i++)
2397 jpeg_info.comp_info[i].h_samp_factor=1;
2398 jpeg_info.comp_info[i].v_samp_factor=1;
2400 option=GetImageArtifact(image,"jpeg:q-table");
2401 if (option != (const char *) NULL)
2407 Custom quantization tables.
2409 table=GetQuantizationTable(option,"0",exception);
2410 if (table != (QuantizationTable *) NULL)
2412 for (i=0; i < MAX_COMPONENTS; i++)
2413 jpeg_add_quant_table(&jpeg_info,i,table->levels,
2414 jpeg_quality_scaling(quality),0);
2415 table=DestroyQuantizationTable(table);
2417 table=GetQuantizationTable(option,"1",exception);
2418 if (table != (QuantizationTable *) NULL)
2420 for (i=1; i < MAX_COMPONENTS; i++)
2421 jpeg_add_quant_table(&jpeg_info,i,table->levels,
2422 jpeg_quality_scaling(quality),0);
2423 table=DestroyQuantizationTable(table);
2425 table=GetQuantizationTable(option,"2",exception);
2426 if (table != (QuantizationTable *) NULL)
2428 for (i=2; i < MAX_COMPONENTS; i++)
2429 jpeg_add_quant_table(&jpeg_info,i,table->levels,
2430 jpeg_quality_scaling(quality),0);
2431 table=DestroyQuantizationTable(table);
2433 table=GetQuantizationTable(option,"3",exception);
2434 if (table != (QuantizationTable *) NULL)
2436 for (i=3; i < MAX_COMPONENTS; i++)
2437 jpeg_add_quant_table(&jpeg_info,i,table->levels,
2438 jpeg_quality_scaling(quality),0);
2439 table=DestroyQuantizationTable(table);
2442 jpeg_start_compress(&jpeg_info,MagickTrue);
2443 if (image->debug != MagickFalse)
2445 if (image->storage_class == PseudoClass)
2446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2447 "Storage class: PseudoClass");
2449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2450 "Storage class: DirectClass");
2451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2452 (double) image->depth);
2453 if (image->colors != 0)
2454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2455 "Number of colors: %.20g",(double) image->colors);
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2458 "Number of colors: unspecified");
2459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2460 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2461 switch (image->colorspace)
2463 case CMYKColorspace:
2465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2466 "Storage class: DirectClass");
2467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2468 "Colorspace: CMYK");
2471 case YCbCrColorspace:
2472 case Rec601YCbCrColorspace:
2473 case Rec709YCbCrColorspace:
2475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2476 "Colorspace: YCbCr");
2482 switch (image->colorspace)
2484 case CMYKColorspace:
2486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2487 "Colorspace: CMYK");
2488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2489 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2490 jpeg_info.comp_info[0].h_samp_factor,
2491 jpeg_info.comp_info[0].v_samp_factor,
2492 jpeg_info.comp_info[1].h_samp_factor,
2493 jpeg_info.comp_info[1].v_samp_factor,
2494 jpeg_info.comp_info[2].h_samp_factor,
2495 jpeg_info.comp_info[2].v_samp_factor,
2496 jpeg_info.comp_info[3].h_samp_factor,
2497 jpeg_info.comp_info[3].v_samp_factor);
2500 case GRAYColorspace:
2502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2503 "Colorspace: GRAY");
2504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2505 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2506 jpeg_info.comp_info[0].v_samp_factor);
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 "Image colorspace is RGB");
2513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2514 "Sampling factors: %dx%d,%dx%d,%dx%d",
2515 jpeg_info.comp_info[0].h_samp_factor,
2516 jpeg_info.comp_info[0].v_samp_factor,
2517 jpeg_info.comp_info[1].h_samp_factor,
2518 jpeg_info.comp_info[1].v_samp_factor,
2519 jpeg_info.comp_info[2].h_samp_factor,
2520 jpeg_info.comp_info[2].v_samp_factor);
2523 case YCbCrColorspace:
2524 case Rec601YCbCrColorspace:
2525 case Rec709YCbCrColorspace:
2527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2528 "Colorspace: YCbCr");
2529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2530 "Sampling factors: %dx%d,%dx%d,%dx%d",
2531 jpeg_info.comp_info[0].h_samp_factor,
2532 jpeg_info.comp_info[0].v_samp_factor,
2533 jpeg_info.comp_info[1].h_samp_factor,
2534 jpeg_info.comp_info[1].v_samp_factor,
2535 jpeg_info.comp_info[2].h_samp_factor,
2536 jpeg_info.comp_info[2].v_samp_factor);
2541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2545 jpeg_info.comp_info[0].h_samp_factor,
2546 jpeg_info.comp_info[0].v_samp_factor,
2547 jpeg_info.comp_info[1].h_samp_factor,
2548 jpeg_info.comp_info[1].v_samp_factor,
2549 jpeg_info.comp_info[2].h_samp_factor,
2550 jpeg_info.comp_info[2].v_samp_factor,
2551 jpeg_info.comp_info[3].h_samp_factor,
2552 jpeg_info.comp_info[3].v_samp_factor);
2558 Write JPEG profiles.
2560 value=GetImageProperty(image,"comment",exception);
2561 if (value != (char *) NULL)
2562 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
2563 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2564 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2565 if (image->profiles != (void *) NULL)
2566 WriteProfile(&jpeg_info,image);
2568 Convert MIFF to JPEG raster pixels.
2570 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2571 jpeg_info.input_components*sizeof(*jpeg_pixels));
2572 if (jpeg_pixels == (JSAMPLE *) NULL)
2573 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2574 if (setjmp(error_manager.error_recovery) != 0)
2576 jpeg_destroy_compress(&jpeg_info);
2577 if (jpeg_pixels != (JSAMPLE *) NULL)
2578 jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
2579 (void) CloseBlob(image);
2580 return(MagickFalse);
2582 scanline[0]=(JSAMPROW) jpeg_pixels;
2583 if (jpeg_info.data_precision <= 8)
2585 if ((jpeg_info.in_color_space == JCS_RGB) ||
2586 (jpeg_info.in_color_space == JCS_YCbCr))
2587 for (y=0; y < (ssize_t) image->rows; y++)
2589 register const Quantum
2595 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2596 if (p == (const Quantum *) NULL)
2599 for (x=0; x < (ssize_t) image->columns; x++)
2601 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2602 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2603 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
2604 p+=GetPixelChannels(image);
2606 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2607 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2609 if (status == MagickFalse)
2613 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2614 for (y=0; y < (ssize_t) image->rows; y++)
2616 register const Quantum
2622 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2623 if (p == (const Quantum *) NULL)
2626 for (x=0; x < (ssize_t) image->columns; x++)
2628 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2630 p+=GetPixelChannels(image);
2632 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2633 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2635 if (status == MagickFalse)
2639 for (y=0; y < (ssize_t) image->rows; y++)
2641 register const Quantum
2647 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2648 if (p == (const Quantum *) NULL)
2651 for (x=0; x < (ssize_t) image->columns; x++)
2654 Convert DirectClass packets to contiguous CMYK scanlines.
2656 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2657 GetPixelCyan(image,p))));
2658 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2659 GetPixelMagenta(image,p))));
2660 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2661 GetPixelYellow(image,p))));
2662 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2663 GetPixelBlack(image,p))));
2664 p+=GetPixelChannels(image);
2666 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2667 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2669 if (status == MagickFalse)
2674 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2675 for (y=0; y < (ssize_t) image->rows; y++)
2677 register const Quantum
2683 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2684 if (p == (const Quantum *) NULL)
2687 for (x=0; x < (ssize_t) image->columns; x++)
2689 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(
2691 p+=GetPixelChannels(image);
2693 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2694 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2696 if (status == MagickFalse)
2700 if ((jpeg_info.in_color_space == JCS_RGB) ||
2701 (jpeg_info.in_color_space == JCS_YCbCr))
2702 for (y=0; y < (ssize_t) image->rows; y++)
2704 register const Quantum
2710 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2711 if (p == (const Quantum *) NULL)
2714 for (x=0; x < (ssize_t) image->columns; x++)
2716 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2717 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2718 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
2719 p+=GetPixelChannels(image);
2721 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2722 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2724 if (status == MagickFalse)
2728 for (y=0; y < (ssize_t) image->rows; y++)
2730 register const Quantum
2736 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2737 if (p == (const Quantum *) NULL)
2740 for (x=0; x < (ssize_t) image->columns; x++)
2743 Convert DirectClass packets to contiguous CMYK scanlines.
2745 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2746 GetPixelRed(image,p)) >> 4));
2747 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2748 GetPixelGreen(image,p)) >> 4));
2749 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2750 GetPixelBlue(image,p)) >> 4));
2751 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2752 GetPixelBlack(image,p)) >> 4));
2753 p+=GetPixelChannels(image);
2755 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2756 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2758 if (status == MagickFalse)
2761 if (y == (ssize_t) image->rows)
2762 jpeg_finish_compress(&jpeg_info);
2764 Relinquish resources.
2766 jpeg_destroy_compress(&jpeg_info);
2767 jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
2768 (void) CloseBlob(image);