2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % JJJJJ PPPP EEEEE GGGG %
13 % Read/Write JPEG Image Format %
20 % Copyright 1999-2011 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 "magick/studio.h"
47 #include "magick/blob.h"
48 #include "magick/blob-private.h"
49 #include "magick/cache.h"
50 #include "magick/color.h"
51 #include "magick/colormap-private.h"
52 #include "magick/color-private.h"
53 #include "magick/colormap.h"
54 #include "magick/colorspace.h"
55 #include "magick/constitute.h"
56 #include "magick/exception.h"
57 #include "magick/exception-private.h"
58 #include "magick/geometry.h"
59 #include "magick/image.h"
60 #include "magick/image-private.h"
61 #include "magick/list.h"
62 #include "magick/log.h"
63 #include "magick/magick.h"
64 #include "magick/memory_.h"
65 #include "magick/module.h"
66 #include "magick/monitor.h"
67 #include "magick/monitor-private.h"
68 #include "magick/option.h"
69 #include "magick/profile.h"
70 #include "magick/property.h"
71 #include "magick/quantum-private.h"
72 #include "magick/resource_.h"
73 #include "magick/splay-tree.h"
74 #include "magick/static.h"
75 #include "magick/string_.h"
76 #include "magick/string-private.h"
77 #include "magick/utility.h"
79 #if defined(MAGICKCORE_JPEG_DELEGATE)
80 #define JPEG_INTERNAL_OPTIONS
81 #if defined(__MINGW32__)
82 # define XMD_H 1 /* Avoid conflicting typedef for INT32 */
83 typedef unsigned char boolean;
93 #define ICC_MARKER (JPEG_APP0+2)
94 #define ICC_PROFILE "ICC_PROFILE"
95 #define IPTC_MARKER (JPEG_APP0+13)
96 #define XML_MARKER (JPEG_APP0+1)
97 #define MaxBufferExtent 8192
100 Typedef declarations.
102 #if defined(MAGICKCORE_JPEG_DELEGATE)
103 typedef struct _DestinationManager
105 struct jpeg_destination_mgr
113 } DestinationManager;
115 typedef struct _ErrorManager
124 typedef struct _SourceManager
126 struct jpeg_source_mgr
141 Forward declarations.
143 #if defined(MAGICKCORE_JPEG_DELEGATE)
144 static MagickBooleanType
145 WriteJPEGImage(const ImageInfo *,Image *);
149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159 % IsJPEG() returns MagickTrue if the image format type, identified by the
160 % magick string, is JPEG.
162 % The format of the IsJPEG method is:
164 % MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
166 % A description of each parameter follows:
168 % o magick: compare image format pattern against these bytes.
170 % o length: Specifies the length of the magick string.
173 static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
177 if (memcmp(magick,"\377\330\377",3) == 0)
182 #if defined(MAGICKCORE_JPEG_DELEGATE)
184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188 % R e a d J P E G I m a g e %
192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
194 % ReadJPEGImage() reads a JPEG image file and returns it. It allocates
195 % the memory necessary for the new Image structure and returns a pointer to
198 % The format of the ReadJPEGImage method is:
200 % Image *ReadJPEGImage(const ImageInfo *image_info,
201 % ExceptionInfo *exception)
203 % A description of each parameter follows:
205 % o image_info: the image info.
207 % o exception: return any errors or warnings in this structure.
211 static MagickBooleanType EmitMessage(j_common_ptr jpeg_info,int level)
214 message[JMSG_LENGTH_MAX];
222 (jpeg_info->err->format_message)(jpeg_info,message);
223 error_manager=(ErrorManager *) jpeg_info->client_data;
224 image=error_manager->image;
227 if ((jpeg_info->err->num_warnings == 0) ||
228 (jpeg_info->err->trace_level >= 3))
229 ThrowBinaryException(CorruptImageWarning,(char *) message,
231 jpeg_info->err->num_warnings++;
234 if (jpeg_info->err->trace_level >= level)
235 ThrowBinaryException(CoderError,(char *) message,image->filename);
239 static boolean FillInputBuffer(j_decompress_ptr cinfo)
244 source=(SourceManager *) cinfo->src;
245 source->manager.bytes_in_buffer=(size_t)
246 ReadBlob(source->image,MaxBufferExtent,source->buffer);
247 if (source->manager.bytes_in_buffer == 0)
249 if (source->start_of_blob != 0)
250 ERREXIT(cinfo,JERR_INPUT_EMPTY);
251 WARNMS(cinfo,JWRN_JPEG_EOF);
252 source->buffer[0]=(JOCTET) 0xff;
253 source->buffer[1]=(JOCTET) JPEG_EOI;
254 source->manager.bytes_in_buffer=2;
256 source->manager.next_input_byte=source->buffer;
257 source->start_of_blob=FALSE;
261 static int GetCharacter(j_decompress_ptr jpeg_info)
263 if (jpeg_info->src->bytes_in_buffer == 0)
264 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
265 jpeg_info->src->bytes_in_buffer--;
266 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
269 static void InitializeSource(j_decompress_ptr cinfo)
274 source=(SourceManager *) cinfo->src;
275 source->start_of_blob=TRUE;
278 static MagickBooleanType IsITUFaxImage(const Image *image)
286 profile=GetImageProfile(image,"8bim");
287 if (profile == (const StringInfo *) NULL)
289 if (GetStringInfoLength(profile) < 5)
291 datum=GetStringInfoDatum(profile);
292 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
293 (datum[3] == 0x41) && (datum[4] == 0x58))
298 static void JPEGErrorHandler(j_common_ptr jpeg_info)
303 (void) EmitMessage(jpeg_info,0);
304 error_manager=(ErrorManager *) jpeg_info->client_data;
305 longjmp(error_manager->error_recovery,1);
308 static boolean ReadComment(j_decompress_ptr jpeg_info)
329 Determine length of comment.
331 error_manager=(ErrorManager *) jpeg_info->client_data;
332 image=error_manager->image;
333 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
334 length+=GetCharacter(jpeg_info);
338 comment=(char *) NULL;
339 if (~length >= MaxTextExtent)
340 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
342 if (comment == (char *) NULL)
343 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
348 i=(ssize_t) length-1;
349 for (p=comment; i-- >= 0; p++)
350 *p=(char) GetCharacter(jpeg_info);
352 (void) SetImageProperty(image,"comment",comment);
353 comment=DestroyString(comment);
357 static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
374 register unsigned char
387 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
388 length+=(size_t) GetCharacter(jpeg_info);
393 (void) GetCharacter(jpeg_info);
396 for (i=0; i < 12; i++)
397 magick[i]=(char) GetCharacter(jpeg_info);
398 if (LocaleCompare(magick,ICC_PROFILE) != 0)
401 Not a ICC profile, return.
403 for (i=0; i < (ssize_t) (length-12); i++)
404 (void) GetCharacter(jpeg_info);
407 (void) GetCharacter(jpeg_info); /* id */
408 (void) GetCharacter(jpeg_info); /* markers */
410 error_manager=(ErrorManager *) jpeg_info->client_data;
411 image=error_manager->image;
412 profile=AcquireStringInfo(length);
413 if (profile == (StringInfo *) NULL)
414 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
416 p=GetStringInfoDatum(profile);
417 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
418 *p++=(unsigned char) GetCharacter(jpeg_info);
419 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
420 if (icc_profile != (StringInfo *) NULL)
422 ConcatenateStringInfo(icc_profile,profile);
423 profile=DestroyStringInfo(profile);
427 status=SetImageProfile(image,"icc",profile);
428 profile=DestroyStringInfo(profile);
429 if (status == MagickFalse)
430 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
433 if (image->debug != MagickFalse)
434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
435 "Profile: ICC, %.20g bytes",(double) length);
439 static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
442 magick[MaxTextExtent];
456 register unsigned char
467 Determine length of binary data stored here.
469 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
470 length+=(size_t) GetCharacter(jpeg_info);
475 (void) GetCharacter(jpeg_info);
479 Validate that this was written as a Photoshop resource format slug.
481 for (i=0; i < 10; i++)
482 magick[i]=(char) GetCharacter(jpeg_info);
487 if (LocaleCompare(magick,"Photoshop ") != 0)
490 Not a IPTC profile, return.
492 for (i=0; i < (ssize_t) length; i++)
493 (void) GetCharacter(jpeg_info);
497 Remove the version number.
499 for (i=0; i < 4; i++)
500 (void) GetCharacter(jpeg_info);
506 error_manager=(ErrorManager *) jpeg_info->client_data;
507 image=error_manager->image;
508 profile=AcquireStringInfo(length);
509 if (profile == (StringInfo *) NULL)
510 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
512 p=GetStringInfoDatum(profile);
513 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
514 *p++=(unsigned char) GetCharacter(jpeg_info);
515 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
516 if (iptc_profile != (StringInfo *) NULL)
518 ConcatenateStringInfo(iptc_profile,profile);
519 profile=DestroyStringInfo(profile);
523 status=SetImageProfile(image,"8bim",profile);
524 profile=DestroyStringInfo(profile);
525 if (status == MagickFalse)
526 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
529 if (image->debug != MagickFalse)
530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
531 "Profile: iptc, %.20g bytes",(double) length);
535 static boolean ReadProfile(j_decompress_ptr jpeg_info)
555 register unsigned char
565 Read generic profile.
567 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
568 length+=(size_t) GetCharacter(jpeg_info);
572 marker=jpeg_info->unread_marker-JPEG_APP0;
573 (void) FormatMagickString(name,MaxTextExtent,"APP%d",marker);
574 error_manager=(ErrorManager *) jpeg_info->client_data;
575 image=error_manager->image;
576 profile=AcquireStringInfo(length);
577 if (profile == (StringInfo *) NULL)
578 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
580 p=GetStringInfoDatum(profile);
581 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
582 *p++=(unsigned char) GetCharacter(jpeg_info);
585 p=GetStringInfoDatum(profile);
586 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
587 (void) CopyMagickString(name,"exif",MaxTextExtent);
588 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
594 Extract namespace from XMP profile.
596 p=GetStringInfoDatum(profile);
597 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
603 if (j < (ssize_t) GetStringInfoLength(profile))
604 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
605 (void) CopyMagickString(name,"xmp",MaxTextExtent);
608 status=SetImageProfile(image,name,profile);
609 profile=DestroyStringInfo(profile);
610 if (status == MagickFalse)
611 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
613 if (image->debug != MagickFalse)
614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
615 "Profile: %s, %.20g bytes",name,(double) length);
619 static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
624 if (number_bytes <= 0)
626 source=(SourceManager *) cinfo->src;
627 while (number_bytes > (long) source->manager.bytes_in_buffer)
629 number_bytes-=(long) source->manager.bytes_in_buffer;
630 (void) FillInputBuffer(cinfo);
632 source->manager.next_input_byte+=number_bytes;
633 source->manager.bytes_in_buffer-=number_bytes;
636 static void TerminateSource(j_decompress_ptr cinfo)
641 static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
646 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
647 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
648 source=(SourceManager *) cinfo->src;
649 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
650 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
651 source=(SourceManager *) cinfo->src;
652 source->manager.init_source=InitializeSource;
653 source->manager.fill_input_buffer=FillInputBuffer;
654 source->manager.skip_input_data=SkipInputData;
655 source->manager.resync_to_restart=jpeg_resync_to_restart;
656 source->manager.term_source=TerminateSource;
657 source->manager.bytes_in_buffer=0;
658 source->manager.next_input_byte=NULL;
662 static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
665 image->quality=UndefinedCompressionQuality;
666 #if defined(D_PROGRESSIVE_SUPPORTED)
667 if (image->compression == LosslessJPEGCompression)
670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
671 "Quality: 100 (lossless)");
685 Determine the JPEG compression quality from the quantization tables.
688 for (i=0; i < NUM_QUANT_TBLS; i++)
690 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
691 for (j=0; j < DCTSIZE2; j++)
692 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
694 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
695 (jpeg_info->quant_tbl_ptrs[1] != NULL))
700 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
701 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
702 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
703 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
704 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
705 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
706 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
707 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
708 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
709 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
714 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
715 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
716 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
717 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
718 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
719 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
720 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
721 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
722 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
723 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
724 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
728 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
729 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
730 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
731 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
732 for (i=0; i < 100; i++)
734 if ((qvalue < hash[i]) && (sum < sums[i]))
736 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
737 image->quality=(size_t) i+1;
738 if (image->debug != MagickFalse)
739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
740 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
741 (sum <= sums[i]) ? "exact" : "approximate");
746 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
751 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
752 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
753 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
754 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
755 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
756 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
757 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
758 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
759 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
760 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
765 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
766 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
767 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
768 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
769 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
770 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
771 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
772 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
773 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
774 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
775 667, 592, 518, 441, 369, 292, 221, 151, 86,
779 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
780 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
781 for (i=0; i < 100; i++)
783 if ((qvalue < hash[i]) && (sum < sums[i]))
785 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
786 image->quality=(size_t) i+1;
787 if (image->debug != MagickFalse)
788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
789 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
790 (sum <= sums[i]) ? "exact" : "approximate");
797 static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image)
800 sampling_factor[MaxTextExtent];
802 switch (jpeg_info->out_color_space)
806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
807 (void) FormatMagickString(sampling_factor,MaxTextExtent,
808 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
809 jpeg_info->comp_info[0].v_samp_factor,
810 jpeg_info->comp_info[1].h_samp_factor,
811 jpeg_info->comp_info[1].v_samp_factor,
812 jpeg_info->comp_info[2].h_samp_factor,
813 jpeg_info->comp_info[2].v_samp_factor,
814 jpeg_info->comp_info[3].h_samp_factor,
815 jpeg_info->comp_info[3].v_samp_factor);
820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
821 "Colorspace: GRAYSCALE");
822 (void) FormatMagickString(sampling_factor,MaxTextExtent,"%dx%d",
823 jpeg_info->comp_info[0].h_samp_factor,
824 jpeg_info->comp_info[0].v_samp_factor);
829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
830 (void) FormatMagickString(sampling_factor,MaxTextExtent,
831 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
832 jpeg_info->comp_info[0].v_samp_factor,
833 jpeg_info->comp_info[1].h_samp_factor,
834 jpeg_info->comp_info[1].v_samp_factor,
835 jpeg_info->comp_info[2].h_samp_factor,
836 jpeg_info->comp_info[2].v_samp_factor);
841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
842 jpeg_info->out_color_space);
843 (void) FormatMagickString(sampling_factor,MaxTextExtent,
844 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
845 jpeg_info->comp_info[0].v_samp_factor,
846 jpeg_info->comp_info[1].h_samp_factor,
847 jpeg_info->comp_info[1].v_samp_factor,
848 jpeg_info->comp_info[2].h_samp_factor,
849 jpeg_info->comp_info[2].v_samp_factor,
850 jpeg_info->comp_info[3].h_samp_factor,
851 jpeg_info->comp_info[3].v_samp_factor);
855 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor);
856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
860 static Image *ReadJPEGImage(const ImageInfo *image_info,
861 ExceptionInfo *exception)
864 value[MaxTextExtent];
897 struct jpeg_decompress_struct
900 struct jpeg_error_mgr
913 assert(image_info != (const ImageInfo *) NULL);
914 assert(image_info->signature == MagickSignature);
915 if (image_info->debug != MagickFalse)
916 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
917 image_info->filename);
918 assert(exception != (ExceptionInfo *) NULL);
919 assert(exception->signature == MagickSignature);
920 debug=IsEventLogging();
922 image=AcquireImage(image_info);
923 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
924 if (status == MagickFalse)
926 image=DestroyImageList(image);
927 return((Image *) NULL);
930 Initialize JPEG parameters.
932 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
933 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
934 jpeg_info.err=jpeg_std_error(&jpeg_error);
935 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
936 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
937 jpeg_pixels=(JSAMPLE *) NULL;
938 error_manager.image=image;
939 if (setjmp(error_manager.error_recovery) != 0)
941 jpeg_destroy_decompress(&jpeg_info);
942 (void) CloseBlob(image);
943 number_pixels=(MagickSizeType) image->columns*image->rows;
944 if (number_pixels != 0)
945 return(GetFirstImageInList(image));
946 InheritException(exception,&image->exception);
947 return(DestroyImage(image));
949 jpeg_info.client_data=(void *) &error_manager;
950 jpeg_create_decompress(&jpeg_info);
951 JPEGSourceManager(&jpeg_info,image);
952 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
953 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
954 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
955 for (i=1; i < 16; i++)
956 if ((i != 2) && (i != 13) && (i != 14))
957 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
958 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
959 if ((image_info->colorspace == YCbCrColorspace) ||
960 (image_info->colorspace == Rec601YCbCrColorspace) ||
961 (image_info->colorspace == Rec709YCbCrColorspace))
962 jpeg_info.out_color_space=JCS_YCbCr;
963 if (IsITUFaxImage(image) != MagickFalse)
965 image->colorspace=LabColorspace;
966 jpeg_info.out_color_space=JCS_YCbCr;
969 if (jpeg_info.out_color_space == JCS_CMYK)
970 image->colorspace=CMYKColorspace;
972 Set image resolution.
975 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
976 (jpeg_info.Y_density != 1))
978 image->x_resolution=(double) jpeg_info.X_density;
979 image->y_resolution=(double) jpeg_info.Y_density;
980 units=(size_t) jpeg_info.density_unit;
983 image->units=PixelsPerInchResolution;
985 image->units=PixelsPerCentimeterResolution;
986 number_pixels=(MagickSizeType) image->columns*image->rows;
987 option=GetImageOption(image_info,"jpeg:size");
988 if (option != (const char *) NULL)
1002 flags=ParseGeometry(option,&geometry_info);
1003 if ((flags & SigmaValue) == 0)
1004 geometry_info.sigma=geometry_info.rho;
1005 jpeg_calc_output_dimensions(&jpeg_info);
1006 image->magick_columns=jpeg_info.output_width;
1007 image->magick_rows=jpeg_info.output_height;
1009 if (geometry_info.rho != 0.0)
1010 scale_factor=jpeg_info.output_width/geometry_info.rho;
1011 if ((geometry_info.sigma != 0.0) &&
1012 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1013 scale_factor=jpeg_info.output_height/geometry_info.sigma;
1014 jpeg_info.scale_num=1U;
1015 jpeg_info.scale_denom=(unsigned int) scale_factor;
1016 jpeg_calc_output_dimensions(&jpeg_info);
1017 if (image->debug != MagickFalse)
1018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1019 "Scale factor: %.20g",(double) scale_factor);
1021 precision=(size_t) jpeg_info.data_precision;
1022 #if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1023 #if defined(D_LOSSLESS_SUPPORTED)
1024 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1025 JPEGInterlace : NoInterlace;
1026 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1027 LosslessJPEGCompression : JPEGCompression;
1028 if (jpeg_info.data_precision > 8)
1029 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1030 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1032 if (jpeg_info.data_precision == 16)
1033 jpeg_info.data_precision=12;
1035 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1037 image->compression=JPEGCompression;
1040 image->compression=JPEGCompression;
1041 image->interlace=JPEGInterlace;
1043 if ((image_info->colors > 8) && (image_info->colors <= 256))
1046 Let the JPEG library quantize for us.
1048 jpeg_info.quantize_colors=MagickTrue;
1049 jpeg_info.desired_number_of_colors=(int) image_info->colors;
1051 option=GetImageOption(image_info,"jpeg:block-smoothing");
1052 if (option != (const char *) NULL)
1054 jpeg_info.do_block_smoothing=MagickFalse;
1055 if (IsMagickTrue(option) != MagickFalse)
1056 jpeg_info.do_block_smoothing=MagickTrue;
1058 option=GetImageOption(image_info,"jpeg:dct-method");
1059 if (option != (const char *) NULL)
1065 if (LocaleCompare(option,"default") == 0)
1066 jpeg_info.dct_method=JDCT_DEFAULT;
1072 if (LocaleCompare(option,"fastest") == 0)
1073 jpeg_info.dct_method=JDCT_FASTEST;
1074 if (LocaleCompare(option,"float") == 0)
1075 jpeg_info.dct_method=JDCT_FLOAT;
1081 if (LocaleCompare(option,"ifast") == 0)
1082 jpeg_info.dct_method=JDCT_IFAST;
1083 if (LocaleCompare(option,"islow") == 0)
1084 jpeg_info.dct_method=JDCT_ISLOW;
1088 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1089 if (option != (const char *) NULL)
1091 jpeg_info.do_fancy_upsampling=MagickFalse;
1092 if (IsMagickTrue(option) != MagickFalse)
1093 jpeg_info.do_fancy_upsampling=MagickTrue;
1095 (void) jpeg_start_decompress(&jpeg_info);
1096 image->columns=jpeg_info.output_width;
1097 image->rows=jpeg_info.output_height;
1098 image->depth=(size_t) jpeg_info.data_precision;
1099 if (jpeg_info.out_color_space == JCS_YCbCr)
1100 image->colorspace=YCbCrColorspace;
1101 if (jpeg_info.out_color_space == JCS_CMYK)
1102 image->colorspace=CMYKColorspace;
1103 if ((image_info->colors != 0) && (image_info->colors <= 256))
1104 if (AcquireImageColormap(image,image_info->colors) == MagickFalse)
1105 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1106 if ((jpeg_info.output_components == 1) &&
1107 (jpeg_info.quantize_colors == MagickFalse))
1112 colors=(size_t) GetQuantumRange(image->depth)+1;
1113 if (AcquireImageColormap(image,colors) == MagickFalse)
1114 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1116 if (image->debug != MagickFalse)
1118 if (image->interlace != NoInterlace)
1119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1120 "Interlace: progressive");
1122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1123 "Interlace: nonprogressive");
1124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1125 (int) jpeg_info.data_precision);
1126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1127 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1129 JPEGSetImageQuality(&jpeg_info,image);
1130 JPEGSetImageSamplingFactor(&jpeg_info,image);
1131 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
1132 jpeg_info.out_color_space);
1133 (void) SetImageProperty(image,"jpeg:colorspace",value);
1134 if (image_info->ping != MagickFalse)
1136 jpeg_destroy_decompress(&jpeg_info);
1137 (void) CloseBlob(image);
1138 return(GetFirstImageInList(image));
1140 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1141 jpeg_info.output_components*sizeof(JSAMPLE));
1142 if (jpeg_pixels == (JSAMPLE *) NULL)
1143 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1145 Convert JPEG pixels to pixel packets.
1147 if (setjmp(error_manager.error_recovery) != 0)
1149 if (jpeg_pixels != (unsigned char *) NULL)
1150 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1151 jpeg_destroy_decompress(&jpeg_info);
1152 (void) CloseBlob(image);
1153 number_pixels=(MagickSizeType) image->columns*image->rows;
1154 if (number_pixels != 0)
1155 return(GetFirstImageInList(image));
1156 return(DestroyImage(image));
1158 if (jpeg_info.quantize_colors != MagickFalse)
1160 image->colors=(size_t) jpeg_info.actual_number_of_colors;
1161 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
1162 for (i=0; i < (ssize_t) image->colors; i++)
1164 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1165 image->colormap[i].green=image->colormap[i].red;
1166 image->colormap[i].blue=image->colormap[i].red;
1167 image->colormap[i].opacity=OpaqueOpacity;
1170 for (i=0; i < (ssize_t) image->colors; i++)
1172 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1173 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1174 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
1175 image->colormap[i].opacity=OpaqueOpacity;
1178 scanline[0]=(JSAMPROW) jpeg_pixels;
1179 for (y=0; y < (ssize_t) image->rows; y++)
1181 register IndexPacket
1187 register PixelPacket
1190 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1192 (void) ThrowMagickException(exception,GetMagickModule(),
1193 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1197 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1198 if (q == (PixelPacket *) NULL)
1200 indexes=GetAuthenticIndexQueue(image);
1201 if (jpeg_info.data_precision > 8)
1203 if (jpeg_info.output_components == 1)
1204 for (x=0; x < (ssize_t) image->columns; x++)
1209 if (precision != 16)
1210 pixel=(size_t) GETJSAMPLE(*p);
1212 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
1213 index=ConstrainColormapIndex(image,pixel);
1215 *q++=image->colormap[(int) index];
1219 if (image->colorspace != CMYKColorspace)
1220 for (x=0; x < (ssize_t) image->columns; x++)
1222 q->red=ScaleShortToQuantum((unsigned char)
1223 (GETJSAMPLE(*p++) << 4));
1224 q->green=ScaleShortToQuantum((unsigned char)
1225 (GETJSAMPLE(*p++) << 4));
1226 q->blue=ScaleShortToQuantum((unsigned char)
1227 (GETJSAMPLE(*p++) << 4));
1228 SetOpacityPixelComponent(q,OpaqueOpacity);
1232 for (x=0; x < (ssize_t) image->columns; x++)
1234 q->red=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1235 (GETJSAMPLE(*p++) << 4));
1236 q->green=(Quantum) QuantumRange-ScaleShortToQuantum(
1237 (unsigned char) (GETJSAMPLE(*p++) << 4));
1238 q->blue=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1239 (GETJSAMPLE(*p++) << 4));
1240 SetOpacityPixelComponent(q,OpaqueOpacity);
1241 indexes[x]=(IndexPacket) QuantumRange-ScaleShortToQuantum(
1242 (unsigned char) (GETJSAMPLE(*p++) << 4));
1247 if (jpeg_info.output_components == 1)
1248 for (x=0; x < (ssize_t) image->columns; x++)
1250 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p));
1251 indexes[x]=(IndexPacket) index;
1252 *q++=image->colormap[(int) index];
1256 if (image->colorspace != CMYKColorspace)
1257 for (x=0; x < (ssize_t) image->columns; x++)
1259 q->red=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1260 q->green=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1261 q->blue=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1262 SetOpacityPixelComponent(q,OpaqueOpacity);
1266 for (x=0; x < (ssize_t) image->columns; x++)
1268 q->red=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1270 q->green=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1272 q->blue=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1274 SetOpacityPixelComponent(q,OpaqueOpacity);
1275 indexes[x]=(IndexPacket) QuantumRange-ScaleCharToQuantum(
1276 (unsigned char) GETJSAMPLE(*p++));
1279 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1281 if (SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
1285 Free jpeg resources.
1287 (void) jpeg_finish_decompress(&jpeg_info);
1288 jpeg_destroy_decompress(&jpeg_info);
1289 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1290 (void) CloseBlob(image);
1291 return(GetFirstImageInList(image));
1296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1300 % R e g i s t e r J P E G I m a g e %
1304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1306 % RegisterJPEGImage() adds properties for the JPEG image format to
1307 % the list of supported formats. The properties include the image format
1308 % tag, a method to read and/or write the format, whether the format
1309 % supports the saving of more than one frame to the same file or blob,
1310 % whether the format supports native in-memory I/O, and a brief
1311 % description of the format.
1313 % The format of the RegisterJPEGImage method is:
1315 % size_t RegisterJPEGImage(void)
1318 ModuleExport size_t RegisterJPEGImage(void)
1321 version[MaxTextExtent];
1327 description[] = "Joint Photographic Experts Group JFIF format";
1330 #if defined(JPEG_LIB_VERSION)
1331 (void) FormatMagickString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
1333 entry=SetMagickInfo("JPEG");
1334 entry->thread_support=NoThreadSupport;
1335 #if defined(MAGICKCORE_JPEG_DELEGATE)
1336 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1337 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1339 entry->magick=(IsImageFormatHandler *) IsJPEG;
1340 entry->adjoin=MagickFalse;
1341 entry->description=ConstantString(description);
1342 if (*version != '\0')
1343 entry->version=ConstantString(version);
1344 entry->module=ConstantString("JPEG");
1345 (void) RegisterMagickInfo(entry);
1346 entry=SetMagickInfo("JPG");
1347 entry->thread_support=NoThreadSupport;
1348 #if defined(MAGICKCORE_JPEG_DELEGATE)
1349 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1350 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1352 entry->adjoin=MagickFalse;
1353 entry->description=ConstantString(description);
1354 if (*version != '\0')
1355 entry->version=ConstantString(version);
1356 entry->module=ConstantString("JPEG");
1357 (void) RegisterMagickInfo(entry);
1358 entry=SetMagickInfo("PJPEG");
1359 entry->thread_support=NoThreadSupport;
1360 #if defined(MAGICKCORE_JPEG_DELEGATE)
1361 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1362 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1364 entry->adjoin=MagickFalse;
1365 entry->description=ConstantString(description);
1366 if (*version != '\0')
1367 entry->version=ConstantString(version);
1368 entry->module=ConstantString("JPEG");
1369 (void) RegisterMagickInfo(entry);
1370 return(MagickImageCoderSignature);
1374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378 % U n r e g i s t e r J P E G I m a g e %
1382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1384 % UnregisterJPEGImage() removes format registrations made by the
1385 % JPEG module from the list of supported formats.
1387 % The format of the UnregisterJPEGImage method is:
1389 % UnregisterJPEGImage(void)
1392 ModuleExport void UnregisterJPEGImage(void)
1394 (void) UnregisterMagickInfo("PJPG");
1395 (void) UnregisterMagickInfo("JPEG");
1396 (void) UnregisterMagickInfo("JPG");
1399 #if defined(MAGICKCORE_JPEG_DELEGATE)
1401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1405 % W r i t e J P E G I m a g e %
1409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1411 % WriteJPEGImage() writes a JPEG image file and returns it. It
1412 % allocates the memory necessary for the new Image structure and returns a
1413 % pointer to the new image.
1415 % The format of the WriteJPEGImage method is:
1417 % MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,Image *image)
1419 % A description of each parameter follows:
1421 % o image_info: the image info.
1423 % o jpeg_image: The image.
1428 static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1433 destination=(DestinationManager *) cinfo->dest;
1434 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1435 MaxBufferExtent,destination->buffer);
1436 if (destination->manager.free_in_buffer != MaxBufferExtent)
1437 ERREXIT(cinfo,JERR_FILE_WRITE);
1438 destination->manager.next_output_byte=destination->buffer;
1442 static void InitializeDestination(j_compress_ptr cinfo)
1447 destination=(DestinationManager *) cinfo->dest;
1448 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1449 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1450 destination->manager.next_output_byte=destination->buffer;
1451 destination->manager.free_in_buffer=MaxBufferExtent;
1454 static inline size_t MagickMin(const size_t x,const size_t y)
1461 static void TerminateDestination(j_compress_ptr cinfo)
1466 destination=(DestinationManager *) cinfo->dest;
1467 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1472 count=WriteBlob(destination->image,MaxBufferExtent-
1473 destination->manager.free_in_buffer,destination->buffer);
1474 if (count != (ssize_t)
1475 (MaxBufferExtent-destination->manager.free_in_buffer))
1476 ERREXIT(cinfo,JERR_FILE_WRITE);
1480 static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1504 Save image profile as a APP marker.
1507 custom_profile=AcquireStringInfo(65535L);
1508 ResetImageProfileIterator(image);
1509 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1511 register unsigned char
1514 profile=GetImageProfile(image,name);
1515 p=GetStringInfoDatum(custom_profile);
1516 if (LocaleCompare(name,"EXIF") == 0)
1517 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
1519 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1520 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1521 (unsigned int) length);
1523 if (LocaleCompare(name,"ICC") == 0)
1525 register unsigned char
1529 p=GetStringInfoDatum(custom_profile);
1530 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
1531 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
1533 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
1534 p[12]=(unsigned char) ((i/65519L)+1);
1535 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1536 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1538 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1539 custom_profile),(unsigned int) (length+tag_length));
1542 if (((LocaleCompare(name,"IPTC") == 0) ||
1543 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1549 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
1551 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
1552 roundup=(size_t) (length & 0x01);
1553 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1555 (void) memcpy(p,"Photoshop 3.0 ",14);
1560 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1562 p[24]=(unsigned char) (length >> 8);
1563 p[25]=(unsigned char) (length & 0xff);
1566 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
1568 p[length+tag_length]='\0';
1569 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1570 custom_profile),(unsigned int) (length+tag_length+roundup));
1573 if (LocaleCompare(name,"XMP") == 0)
1579 Add namespace to XMP profile.
1581 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/");
1582 ConcatenateStringInfo(xmp_profile,profile);
1583 GetStringInfoDatum(xmp_profile)[28]='\0';
1584 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
1586 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1587 jpeg_write_marker(jpeg_info,XML_MARKER,
1588 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1590 xmp_profile=DestroyStringInfo(xmp_profile);
1592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1593 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
1594 name=GetNextImageProfile(image);
1596 custom_profile=DestroyStringInfo(custom_profile);
1599 static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1604 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1605 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1606 destination=(DestinationManager *) cinfo->dest;
1607 destination->manager.init_destination=InitializeDestination;
1608 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1609 destination->manager.term_destination=TerminateDestination;
1610 destination->image=image;
1613 static char **SamplingFactorToList(const char *text)
1630 if (text == (char *) NULL)
1631 return((char **) NULL);
1633 Convert string to an ASCII list.
1636 for (p=text; *p != '\0'; p++)
1639 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1641 if (textlist == (char **) NULL)
1642 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1644 for (i=0; i < (ssize_t) lines; i++)
1646 for (q=(char *) p; *q != '\0'; q++)
1649 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1650 sizeof(*textlist[i]));
1651 if (textlist[i] == (char *) NULL)
1652 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1653 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1658 textlist[i]=(char *) NULL;
1662 static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1691 struct jpeg_compress_struct
1694 struct jpeg_error_mgr
1700 assert(image_info != (const ImageInfo *) NULL);
1701 assert(image_info->signature == MagickSignature);
1702 assert(image != (Image *) NULL);
1703 assert(image->signature == MagickSignature);
1704 if (image->debug != MagickFalse)
1705 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1706 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1707 if (status == MagickFalse)
1710 Initialize JPEG parameters.
1712 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1713 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1714 jpeg_info.client_data=(void *) image;
1715 jpeg_info.err=jpeg_std_error(&jpeg_error);
1716 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
1717 error_manager.image=image;
1718 jpeg_pixels=(JSAMPLE *) NULL;
1719 if (setjmp(error_manager.error_recovery) != 0)
1721 jpeg_destroy_compress(&jpeg_info);
1722 (void) CloseBlob(image);
1723 return(MagickFalse);
1725 jpeg_info.client_data=(void *) &error_manager;
1726 jpeg_create_compress(&jpeg_info);
1727 JPEGDestinationManager(&jpeg_info,image);
1728 if ((image->columns != (unsigned int) image->columns) ||
1729 (image->rows != (unsigned int) image->rows))
1730 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1731 jpeg_info.image_width=(unsigned int) image->columns;
1732 jpeg_info.image_height=(unsigned int) image->rows;
1733 jpeg_info.input_components=3;
1734 jpeg_info.data_precision=8;
1735 jpeg_info.in_color_space=JCS_RGB;
1736 switch (image->colorspace)
1738 case CMYKColorspace:
1740 jpeg_info.input_components=4;
1741 jpeg_info.in_color_space=JCS_CMYK;
1744 case YCbCrColorspace:
1745 case Rec601YCbCrColorspace:
1746 case Rec709YCbCrColorspace:
1748 jpeg_info.in_color_space=JCS_YCbCr;
1751 case GRAYColorspace:
1752 case Rec601LumaColorspace:
1753 case Rec709LumaColorspace:
1755 jpeg_info.input_components=1;
1756 jpeg_info.in_color_space=JCS_GRAYSCALE;
1761 if (image->colorspace != RGBColorspace)
1762 (void) TransformImageColorspace(image,RGBColorspace);
1766 if ((image_info->type != TrueColorType) &&
1767 (IsGrayImage(image,&image->exception) != MagickFalse))
1769 jpeg_info.input_components=1;
1770 jpeg_info.in_color_space=JCS_GRAYSCALE;
1772 jpeg_set_defaults(&jpeg_info);
1773 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1774 jpeg_info.data_precision=8;
1776 if (sizeof(JSAMPLE) > 1)
1777 jpeg_info.data_precision=12;
1778 jpeg_info.density_unit=(UINT8) 1;
1779 if (image->debug != MagickFalse)
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 "Image resolution: %.20g,%.20g",floor(image->x_resolution+0.5),
1782 floor(image->y_resolution+0.5));
1783 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1786 Set image resolution.
1788 jpeg_info.write_JFIF_header=MagickTrue;
1789 jpeg_info.X_density=(UINT16) floor(image->x_resolution+0.5);
1790 jpeg_info.Y_density=(UINT16) floor(image->y_resolution+0.5);
1791 if (image->units == PixelsPerInchResolution)
1792 jpeg_info.density_unit=(UINT8) 1;
1793 if (image->units == PixelsPerCentimeterResolution)
1794 jpeg_info.density_unit=(UINT8) 2;
1796 option=GetImageOption(image_info,"jpeg:dct-method");
1797 if (option != (const char *) NULL)
1803 if (LocaleCompare(option,"default") == 0)
1804 jpeg_info.dct_method=JDCT_DEFAULT;
1810 if (LocaleCompare(option,"fastest") == 0)
1811 jpeg_info.dct_method=JDCT_FASTEST;
1812 if (LocaleCompare(option,"float") == 0)
1813 jpeg_info.dct_method=JDCT_FLOAT;
1819 if (LocaleCompare(option,"ifast") == 0)
1820 jpeg_info.dct_method=JDCT_IFAST;
1821 if (LocaleCompare(option,"islow") == 0)
1822 jpeg_info.dct_method=JDCT_ISLOW;
1826 option=GetImageOption(image_info,"jpeg:optimize-coding");
1827 if (option != (const char *) NULL)
1829 jpeg_info.optimize_coding=MagickFalse;
1830 if (IsMagickTrue(option) != MagickFalse)
1831 jpeg_info.optimize_coding=MagickTrue;
1838 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1839 image->rows*sizeof(JSAMPLE);
1840 if (length == (MagickSizeType) ((size_t) length))
1843 Perform optimization only if available memory resources permit it.
1845 status=AcquireMagickResource(MemoryResource,length);
1846 if (status != MagickFalse)
1847 jpeg_info.optimize_coding=MagickTrue;
1848 RelinquishMagickResource(MemoryResource,length);
1851 #if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1852 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1853 (image_info->interlace != NoInterlace))
1855 if (image->debug != MagickFalse)
1856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1857 "Interlace: progressive");
1858 jpeg_simple_progression(&jpeg_info);
1861 if (image->debug != MagickFalse)
1862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1863 "Interlace: non-progressive");
1865 if (image->debug != MagickFalse)
1866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1867 "Interlace: nonprogressive");
1869 option=GetImageOption(image_info,"jpeg:extent");
1870 if (option != (const char *) NULL)
1878 jpeg_info=CloneImageInfo(image_info);
1879 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1880 if (jpeg_image != (Image *) NULL)
1890 Search for compression quality that does not exceed image extent.
1892 jpeg_info->quality=0;
1893 extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
1894 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1895 (void) AcquireUniqueFilename(jpeg_image->filename);
1897 for (minimum=0; minimum != maximum; )
1899 jpeg_image->quality=minimum+(maximum-minimum)/2;
1900 status=WriteJPEGImage(jpeg_info,jpeg_image);
1901 if (GetBlobSize(jpeg_image) <= extent)
1902 minimum=jpeg_image->quality+1;
1904 maximum=jpeg_image->quality-1;
1906 (void) RelinquishUniqueFileResource(jpeg_image->filename);
1907 image->quality=minimum-1;
1908 jpeg_image=DestroyImage(jpeg_image);
1910 jpeg_info=DestroyImageInfo(jpeg_info);
1912 if ((image_info->compression != LosslessJPEGCompression) &&
1913 (image->quality <= 100))
1915 if (image->quality == UndefinedCompressionQuality)
1916 jpeg_set_quality(&jpeg_info,92,MagickTrue);
1918 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
1919 if (image->debug != MagickFalse)
1920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
1921 (double) image->quality);
1925 #if !defined(C_LOSSLESS_SUPPORTED)
1926 jpeg_set_quality(&jpeg_info,100,MagickTrue);
1927 if (image->debug != MagickFalse)
1928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
1930 if (image->quality < 100)
1931 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1932 CoderWarning,"LosslessToLossyJPEGConversion",image->filename);
1939 predictor=image->quality/100; /* range 1-7 */
1940 point_transform=image->quality % 20; /* range 0-15 */
1941 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
1942 if (image->debug != MagickFalse)
1944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1945 "Compression: lossless");
1946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1947 "Predictor: %d",predictor);
1948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1949 "Point Transform: %d",point_transform);
1954 sampling_factor=(const char *) NULL;
1955 value=GetImageProperty(image,"jpeg:sampling-factor");
1956 if (value != (char *) NULL)
1958 sampling_factor=value;
1959 if (image->debug != MagickFalse)
1960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1961 " Input sampling-factors=%s",sampling_factor);
1963 if (image_info->sampling_factor != (char *) NULL)
1964 sampling_factor=image_info->sampling_factor;
1965 if (sampling_factor == (const char *) NULL)
1967 if (image->quality >= 90)
1968 for (i=0; i < MAX_COMPONENTS; i++)
1970 jpeg_info.comp_info[i].h_samp_factor=1;
1971 jpeg_info.comp_info[i].v_samp_factor=1;
1986 Set sampling factor.
1989 factors=SamplingFactorToList(sampling_factor);
1990 if (factors != (char **) NULL)
1992 for (i=0; i < MAX_COMPONENTS; i++)
1994 if (factors[i] == (char *) NULL)
1996 flags=ParseGeometry(factors[i],&geometry_info);
1997 if ((flags & SigmaValue) == 0)
1998 geometry_info.sigma=geometry_info.rho;
1999 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2000 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2001 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2003 factors=(char **) RelinquishMagickMemory(factors);
2005 for ( ; i < MAX_COMPONENTS; i++)
2007 jpeg_info.comp_info[i].h_samp_factor=1;
2008 jpeg_info.comp_info[i].v_samp_factor=1;
2011 if (jpeg_info.input_components == 1)
2012 for (i=0; i < MAX_COMPONENTS; i++)
2014 jpeg_info.comp_info[i].h_samp_factor=1;
2015 jpeg_info.comp_info[i].v_samp_factor=1;
2017 jpeg_start_compress(&jpeg_info,MagickTrue);
2018 if (image->debug != MagickFalse)
2020 if (image->storage_class == PseudoClass)
2021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2022 "Storage class: PseudoClass");
2024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2025 "Storage class: DirectClass");
2026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2027 (double) image->depth);
2028 if (image->colors != 0)
2029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2030 "Number of colors: %.20g",(double) image->colors);
2032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2033 "Number of colors: unspecified");
2034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2035 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2036 switch (image->colorspace)
2038 case CMYKColorspace:
2040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2041 "Storage class: DirectClass");
2042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2043 "Colorspace: CMYK");
2046 case YCbCrColorspace:
2047 case Rec601YCbCrColorspace:
2048 case Rec709YCbCrColorspace:
2050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2051 "Colorspace: YCbCr");
2057 switch (image->colorspace)
2059 case CMYKColorspace:
2061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2062 "Colorspace: CMYK");
2063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2064 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2065 jpeg_info.comp_info[0].h_samp_factor,
2066 jpeg_info.comp_info[0].v_samp_factor,
2067 jpeg_info.comp_info[1].h_samp_factor,
2068 jpeg_info.comp_info[1].v_samp_factor,
2069 jpeg_info.comp_info[2].h_samp_factor,
2070 jpeg_info.comp_info[2].v_samp_factor,
2071 jpeg_info.comp_info[3].h_samp_factor,
2072 jpeg_info.comp_info[3].v_samp_factor);
2075 case GRAYColorspace:
2076 case Rec601LumaColorspace:
2077 case Rec709LumaColorspace:
2079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2080 "Colorspace: GRAY");
2081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2082 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2083 jpeg_info.comp_info[0].v_samp_factor);
2088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2089 "Image colorspace is RGB");
2090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2091 "Sampling factors: %dx%d,%dx%d,%dx%d",
2092 jpeg_info.comp_info[0].h_samp_factor,
2093 jpeg_info.comp_info[0].v_samp_factor,
2094 jpeg_info.comp_info[1].h_samp_factor,
2095 jpeg_info.comp_info[1].v_samp_factor,
2096 jpeg_info.comp_info[2].h_samp_factor,
2097 jpeg_info.comp_info[2].v_samp_factor);
2100 case YCbCrColorspace:
2101 case Rec601YCbCrColorspace:
2102 case Rec709YCbCrColorspace:
2104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2105 "Colorspace: YCbCr");
2106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2107 "Sampling factors: %dx%d,%dx%d,%dx%d",
2108 jpeg_info.comp_info[0].h_samp_factor,
2109 jpeg_info.comp_info[0].v_samp_factor,
2110 jpeg_info.comp_info[1].h_samp_factor,
2111 jpeg_info.comp_info[1].v_samp_factor,
2112 jpeg_info.comp_info[2].h_samp_factor,
2113 jpeg_info.comp_info[2].v_samp_factor);
2118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2121 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2122 jpeg_info.comp_info[0].h_samp_factor,
2123 jpeg_info.comp_info[0].v_samp_factor,
2124 jpeg_info.comp_info[1].h_samp_factor,
2125 jpeg_info.comp_info[1].v_samp_factor,
2126 jpeg_info.comp_info[2].h_samp_factor,
2127 jpeg_info.comp_info[2].v_samp_factor,
2128 jpeg_info.comp_info[3].h_samp_factor,
2129 jpeg_info.comp_info[3].v_samp_factor);
2135 Write JPEG profiles.
2137 value=GetImageProperty(image,"comment");
2138 if (value != (char *) NULL)
2139 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
2140 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2141 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2142 if (image->profiles != (void *) NULL)
2143 WriteProfile(&jpeg_info,image);
2145 Convert MIFF to JPEG raster pixels.
2147 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2148 jpeg_info.input_components*sizeof(*jpeg_pixels));
2149 if (jpeg_pixels == (JSAMPLE *) NULL)
2150 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2151 if (setjmp(error_manager.error_recovery) != 0)
2153 jpeg_destroy_compress(&jpeg_info);
2154 if (jpeg_pixels != (unsigned char *) NULL)
2155 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2156 (void) CloseBlob(image);
2157 return(MagickFalse);
2159 scanline[0]=(JSAMPROW) jpeg_pixels;
2160 if (jpeg_info.data_precision <= 8)
2162 if ((jpeg_info.in_color_space == JCS_RGB) ||
2163 (jpeg_info.in_color_space == JCS_YCbCr))
2164 for (y=0; y < (ssize_t) image->rows; y++)
2166 register const PixelPacket
2172 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2173 if (p == (const PixelPacket *) NULL)
2176 for (x=0; x < (ssize_t) image->columns; x++)
2178 *q++=(JSAMPLE) ScaleQuantumToChar(GetRedPixelComponent(p));
2179 *q++=(JSAMPLE) ScaleQuantumToChar(GetGreenPixelComponent(p));
2180 *q++=(JSAMPLE) ScaleQuantumToChar(GetBluePixelComponent(p));
2183 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2184 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
2188 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2189 for (y=0; y < (ssize_t) image->rows; y++)
2191 register const PixelPacket
2197 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2198 if (p == (const PixelPacket *) NULL)
2201 for (x=0; x < (ssize_t) image->columns; x++)
2203 *q++=(JSAMPLE) ScaleQuantumToChar(PixelIntensityToQuantum(p));
2206 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2207 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
2211 for (y=0; y < (ssize_t) image->rows; y++)
2213 register const IndexPacket
2216 register const PixelPacket
2222 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2223 if (p == (const PixelPacket *) NULL)
2226 indexes=GetVirtualIndexQueue(image);
2227 for (x=0; x < (ssize_t) image->columns; x++)
2230 Convert DirectClass packets to contiguous CMYK scanlines.
2232 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2233 GetRedPixelComponent(p))));
2234 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2235 GetGreenPixelComponent(p))));
2236 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2237 GetBluePixelComponent(p))));
2238 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2242 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2243 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
2248 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2249 for (y=0; y < (ssize_t) image->rows; y++)
2251 register const PixelPacket
2257 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2258 if (p == (const PixelPacket *) NULL)
2261 for (x=0; x < (ssize_t) image->columns; x++)
2263 *q++=(JSAMPLE) (ScaleQuantumToShort(PixelIntensityToQuantum(p)) >>
2267 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2268 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
2272 if ((jpeg_info.in_color_space == JCS_RGB) ||
2273 (jpeg_info.in_color_space == JCS_YCbCr))
2274 for (y=0; y < (ssize_t) image->rows; y++)
2276 register const PixelPacket
2282 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2283 if (p == (const PixelPacket *) NULL)
2286 for (x=0; x < (ssize_t) image->columns; x++)
2288 *q++=(JSAMPLE) (ScaleQuantumToShort(GetRedPixelComponent(p)) >>
2290 *q++=(JSAMPLE) (ScaleQuantumToShort(GetGreenPixelComponent(p)) >>
2292 *q++=(JSAMPLE) (ScaleQuantumToShort(GetBluePixelComponent(p)) >>
2296 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2297 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2299 if (status == MagickFalse)
2303 for (y=0; y < (ssize_t) image->rows; y++)
2305 register const IndexPacket
2308 register const PixelPacket
2314 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2315 if (p == (const PixelPacket *) NULL)
2318 indexes=GetVirtualIndexQueue(image);
2319 for (x=0; x < (ssize_t) image->columns; x++)
2322 Convert DirectClass packets to contiguous CMYK scanlines.
2324 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2325 GetRedPixelComponent(p)) >> 4));
2326 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2327 GetGreenPixelComponent(p)) >> 4));
2328 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2329 GetBluePixelComponent(p)) >> 4));
2330 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(indexes[x]) >> 4));
2333 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2334 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2336 if (status == MagickFalse)
2339 if (y == (ssize_t) image->rows)
2340 jpeg_finish_compress(&jpeg_info);
2342 Relinquish resources.
2344 jpeg_destroy_compress(&jpeg_info);
2345 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2346 (void) CloseBlob(image);