blob: 46823ca218f53271b79259c9f2d1bdf4b61d631e [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% JJJJJ PPPP EEEEE GGGG %
7% J P P E G %
8% J PPPP EEE G GG %
9% J J P E G G %
10% JJJ P EEEEE GGG %
11% %
12% %
13% Read/Write JPEG Image Format %
14% %
15% Software Design %
cristy5328a4c2013-12-03 11:32:13 +000016% John Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
cristy5328a4c2013-12-03 11:32:13 +000020% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
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. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
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.
39%
40%
41*/
dirk29dd80e2013-10-31 23:11:11 +000042
43
cristy3ed852e2009-09-05 21:47:34 +000044/*
45 Include declarations.
46*/
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/studio.h"
cristyc6348612013-04-05 14:24:24 +000048#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/attribute.h"
50#include "MagickCore/blob.h"
51#include "MagickCore/blob-private.h"
52#include "MagickCore/cache.h"
53#include "MagickCore/color.h"
54#include "MagickCore/colormap-private.h"
55#include "MagickCore/color-private.h"
56#include "MagickCore/colormap.h"
57#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000058#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000059#include "MagickCore/constitute.h"
60#include "MagickCore/exception.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/geometry.h"
63#include "MagickCore/image.h"
64#include "MagickCore/image-private.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/magick.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/module.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/option.h"
cristy53b6cc32014-06-25 13:13:33 +000073#include "MagickCore/option-private.h"
cristy4c08aed2011-07-01 19:47:50 +000074#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/profile.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum-private.h"
78#include "MagickCore/resource_.h"
cristy191ba5c2014-03-16 21:26:40 +000079#include "MagickCore/semaphore.h"
cristy4c08aed2011-07-01 19:47:50 +000080#include "MagickCore/splay-tree.h"
81#include "MagickCore/static.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
cristye40005d2012-03-23 12:18:45 +000084#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000085#include "MagickCore/utility.h"
cristy1b58f252012-03-01 01:41:41 +000086#include "MagickCore/xml-tree.h"
87#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000088#include <setjmp.h>
89#if defined(MAGICKCORE_JPEG_DELEGATE)
90#define JPEG_INTERNAL_OPTIONS
cristy07a3cca2012-12-10 13:09:10 +000091#if defined(__MINGW32__) || defined(__MINGW64__)
cristy3ed852e2009-09-05 21:47:34 +000092# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
93#endif
94#undef HAVE_STDLIB_H
95#include "jpeglib.h"
96#include "jerror.h"
97#endif
dirk29dd80e2013-10-31 23:11:11 +000098
99
cristy3ed852e2009-09-05 21:47:34 +0000100/*
101 Define declarations.
102*/
103#define ICC_MARKER (JPEG_APP0+2)
104#define ICC_PROFILE "ICC_PROFILE"
105#define IPTC_MARKER (JPEG_APP0+13)
106#define XML_MARKER (JPEG_APP0+1)
cristyeb9759e2012-06-07 23:06:29 +0000107#define MaxBufferExtent 16384
dirk29dd80e2013-10-31 23:11:11 +0000108
109
cristy3ed852e2009-09-05 21:47:34 +0000110/*
111 Typedef declarations.
112*/
113#if defined(MAGICKCORE_JPEG_DELEGATE)
114typedef struct _DestinationManager
115{
116 struct jpeg_destination_mgr
117 manager;
118
119 Image
120 *image;
121
122 JOCTET
123 *buffer;
124} DestinationManager;
125
126typedef struct _ErrorManager
127{
cristy018f07f2011-09-04 21:15:19 +0000128 ExceptionInfo
129 *exception;
130
cristy3ed852e2009-09-05 21:47:34 +0000131 Image
132 *image;
133
cristyd28b1dd2011-05-14 20:30:38 +0000134 MagickBooleanType
135 finished;
136
cristyf551aee2012-09-27 21:45:23 +0000137 StringInfo
138 *profile;
139
cristy3ed852e2009-09-05 21:47:34 +0000140 jmp_buf
141 error_recovery;
142} ErrorManager;
143
144typedef struct _SourceManager
145{
146 struct jpeg_source_mgr
147 manager;
148
149 Image
150 *image;
151
152 JOCTET
153 *buffer;
154
155 boolean
156 start_of_blob;
157} SourceManager;
158#endif
cristy1b58f252012-03-01 01:41:41 +0000159
160typedef struct _QuantizationTable
161{
162 char
163 *slot,
164 *description;
165
166 size_t
167 width,
168 height;
169
cristy043f3f32012-03-02 17:37:28 +0000170 double
171 divisor;
172
cristy1b58f252012-03-01 01:41:41 +0000173 unsigned int
cristy1b58f252012-03-01 01:41:41 +0000174 *levels;
175} QuantizationTable;
dirk29dd80e2013-10-31 23:11:11 +0000176
177
cristy3ed852e2009-09-05 21:47:34 +0000178/*
179 Forward declarations.
180*/
181#if defined(MAGICKCORE_JPEG_DELEGATE)
182static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000183 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000184#endif
dirk29dd80e2013-10-31 23:11:11 +0000185
186
cristy3ed852e2009-09-05 21:47:34 +0000187/*
188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189% %
190% %
191% %
192% I s J P E G %
193% %
194% %
195% %
196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197%
198% IsJPEG() returns MagickTrue if the image format type, identified by the
199% magick string, is JPEG.
200%
201% The format of the IsJPEG method is:
202%
203% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
204%
205% A description of each parameter follows:
206%
207% o magick: compare image format pattern against these bytes.
208%
209% o length: Specifies the length of the magick string.
210%
211*/
212static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
213{
214 if (length < 3)
215 return(MagickFalse);
216 if (memcmp(magick,"\377\330\377",3) == 0)
217 return(MagickTrue);
218 return(MagickFalse);
219}
dirk29dd80e2013-10-31 23:11:11 +0000220
221
cristy3ed852e2009-09-05 21:47:34 +0000222#if defined(MAGICKCORE_JPEG_DELEGATE)
223/*
224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
225% %
226% %
227% %
228% R e a d J P E G I m a g e %
229% %
230% %
231% %
232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233%
234% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
235% the memory necessary for the new Image structure and returns a pointer to
236% the new image.
237%
238% The format of the ReadJPEGImage method is:
239%
240% Image *ReadJPEGImage(const ImageInfo *image_info,
241% ExceptionInfo *exception)
242%
243% A description of each parameter follows:
244%
245% o image_info: the image info.
246%
247% o exception: return any errors or warnings in this structure.
248%
249*/
250
cristy3ed852e2009-09-05 21:47:34 +0000251static boolean FillInputBuffer(j_decompress_ptr cinfo)
252{
253 SourceManager
254 *source;
255
256 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000257 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
258 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000259 if (source->manager.bytes_in_buffer == 0)
260 {
cristy453b8202012-06-07 22:57:52 +0000261 if (source->start_of_blob != FALSE)
cristy3ed852e2009-09-05 21:47:34 +0000262 ERREXIT(cinfo,JERR_INPUT_EMPTY);
263 WARNMS(cinfo,JWRN_JPEG_EOF);
264 source->buffer[0]=(JOCTET) 0xff;
265 source->buffer[1]=(JOCTET) JPEG_EOI;
266 source->manager.bytes_in_buffer=2;
267 }
268 source->manager.next_input_byte=source->buffer;
269 source->start_of_blob=FALSE;
270 return(TRUE);
271}
272
273static int GetCharacter(j_decompress_ptr jpeg_info)
274{
275 if (jpeg_info->src->bytes_in_buffer == 0)
276 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
277 jpeg_info->src->bytes_in_buffer--;
278 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
279}
280
281static void InitializeSource(j_decompress_ptr cinfo)
282{
283 SourceManager
284 *source;
285
286 source=(SourceManager *) cinfo->src;
287 source->start_of_blob=TRUE;
288}
289
cristye8dd1302009-11-11 02:45:03 +0000290static MagickBooleanType IsITUFaxImage(const Image *image)
291{
292 const StringInfo
293 *profile;
294
295 const unsigned char
296 *datum;
297
cristyace6aa42009-11-11 03:17:33 +0000298 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000299 if (profile == (const StringInfo *) NULL)
300 return(MagickFalse);
301 if (GetStringInfoLength(profile) < 5)
302 return(MagickFalse);
303 datum=GetStringInfoDatum(profile);
304 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
305 (datum[3] == 0x41) && (datum[4] == 0x58))
306 return(MagickTrue);
307 return(MagickFalse);
308}
309
cristy437c3932011-10-12 18:03:46 +0000310static void JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000311{
cristyd28b1dd2011-05-14 20:30:38 +0000312 char
313 message[JMSG_LENGTH_MAX];
314
cristy3ed852e2009-09-05 21:47:34 +0000315 ErrorManager
316 *error_manager;
317
cristyc82a27b2011-10-21 01:07:16 +0000318 ExceptionInfo
319 *exception;
320
cristyd28b1dd2011-05-14 20:30:38 +0000321 Image
322 *image;
323
324 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000325 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000326 image=error_manager->image;
cristyc82a27b2011-10-21 01:07:16 +0000327 exception=error_manager->exception;
cristy86f33542011-05-21 22:58:33 +0000328 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000329 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
331 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000332 if (error_manager->finished != MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000333 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
334 (char *) message,"`%s'",image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000335 else
cristyc82a27b2011-10-21 01:07:16 +0000336 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
337 (char *) message,"`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000338 longjmp(error_manager->error_recovery,1);
339}
340
cristyd28b1dd2011-05-14 20:30:38 +0000341static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
342{
cristyf162e012012-03-28 12:54:33 +0000343#define JPEGExcessiveWarnings 1000
344
cristyd28b1dd2011-05-14 20:30:38 +0000345 char
346 message[JMSG_LENGTH_MAX];
347
348 ErrorManager
349 *error_manager;
350
cristy018f07f2011-09-04 21:15:19 +0000351 ExceptionInfo
352 *exception;
353
cristyd28b1dd2011-05-14 20:30:38 +0000354 Image
355 *image;
356
357 *message='\0';
358 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000359 exception=error_manager->exception;
cristyd28b1dd2011-05-14 20:30:38 +0000360 image=error_manager->image;
361 if (level < 0)
362 {
363 /*
364 Process warning message.
365 */
366 (jpeg_info->err->format_message)(jpeg_info,message);
cristyf162e012012-03-28 12:54:33 +0000367 if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
368 JPEGErrorHandler(jpeg_info);
cristy8043fb32012-06-28 16:14:12 +0000369 ThrowBinaryException(CorruptImageWarning,(char *) message,
370 image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000371 }
372 else
373 if ((image->debug != MagickFalse) &&
374 (level >= jpeg_info->err->trace_level))
375 {
376 /*
377 Process trace message.
378 */
379 (jpeg_info->err->format_message)(jpeg_info,message);
380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
381 "[%s] JPEG Trace: \"%s\"",image->filename,message);
382 }
383 return(MagickTrue);
384}
385
cristy3ed852e2009-09-05 21:47:34 +0000386static boolean ReadComment(j_decompress_ptr jpeg_info)
387{
cristy3ed852e2009-09-05 21:47:34 +0000388 ErrorManager
389 *error_manager;
390
cristy018f07f2011-09-04 21:15:19 +0000391 ExceptionInfo
392 *exception;
393
cristy3ed852e2009-09-05 21:47:34 +0000394 Image
395 *image;
396
cristyf551aee2012-09-27 21:45:23 +0000397 register unsigned char
cristy3ed852e2009-09-05 21:47:34 +0000398 *p;
399
cristybb503372010-05-27 20:51:26 +0000400 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000401 i;
402
403 size_t
404 length;
405
cristyf551aee2012-09-27 21:45:23 +0000406 StringInfo
407 *comment;
408
cristy3ed852e2009-09-05 21:47:34 +0000409 /*
410 Determine length of comment.
411 */
412 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000413 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000414 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000415 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000416 length+=GetCharacter(jpeg_info);
dirk896a34e2014-09-18 10:57:30 +0000417 if (length <= 2)
cristydf878012014-01-12 23:39:40 +0000418 return(TRUE);
dirk896a34e2014-09-18 10:57:30 +0000419 length-=2;
cristyf551aee2012-09-27 21:45:23 +0000420 comment=BlobToStringInfo((const void *) NULL,length);
421 if (comment == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000422 {
423 (void) ThrowMagickException(exception,GetMagickModule(),
424 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
425 return(FALSE);
426 }
cristy3ed852e2009-09-05 21:47:34 +0000427 /*
428 Read comment.
429 */
cristyf551aee2012-09-27 21:45:23 +0000430 error_manager->profile=comment;
431 p=GetStringInfoDatum(comment);
432 for (i=0; i < (ssize_t) GetStringInfoLength(comment); i++)
433 *p++=(unsigned char) GetCharacter(jpeg_info);
cristy3ed852e2009-09-05 21:47:34 +0000434 *p='\0';
cristyf551aee2012-09-27 21:45:23 +0000435 error_manager->profile=NULL;
436 p=GetStringInfoDatum(comment);
437 (void) SetImageProperty(image,"comment",(const char *) p,exception);
438 comment=DestroyStringInfo(comment);
cristydf878012014-01-12 23:39:40 +0000439 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000440}
441
442static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
443{
444 char
445 magick[12];
446
447 ErrorManager
448 *error_manager;
449
cristy018f07f2011-09-04 21:15:19 +0000450 ExceptionInfo
451 *exception;
452
cristy3ed852e2009-09-05 21:47:34 +0000453 Image
454 *image;
455
456 MagickBooleanType
457 status;
458
cristybb503372010-05-27 20:51:26 +0000459 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000460 i;
461
462 register unsigned char
463 *p;
464
465 size_t
466 length;
467
468 StringInfo
469 *icc_profile,
470 *profile;
471
472 /*
473 Read color profile.
474 */
cristybb503372010-05-27 20:51:26 +0000475 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000476 length+=(size_t) GetCharacter(jpeg_info);
477 length-=2;
478 if (length <= 14)
479 {
480 while (length-- > 0)
481 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000482 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000483 }
484 for (i=0; i < 12; i++)
485 magick[i]=(char) GetCharacter(jpeg_info);
486 if (LocaleCompare(magick,ICC_PROFILE) != 0)
487 {
488 /*
489 Not a ICC profile, return.
490 */
cristybb503372010-05-27 20:51:26 +0000491 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000492 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000493 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000494 }
495 (void) GetCharacter(jpeg_info); /* id */
496 (void) GetCharacter(jpeg_info); /* markers */
497 length-=14;
498 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000499 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000500 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000501 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000502 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000503 {
504 (void) ThrowMagickException(exception,GetMagickModule(),
505 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
506 return(FALSE);
507 }
cristyf551aee2012-09-27 21:45:23 +0000508 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000509 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000510 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000511 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000512 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000513 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
514 if (icc_profile != (StringInfo *) NULL)
515 {
516 ConcatenateStringInfo(icc_profile,profile);
517 profile=DestroyStringInfo(profile);
518 }
519 else
520 {
cristyd15e6592011-10-15 00:13:06 +0000521 status=SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000522 profile=DestroyStringInfo(profile);
523 if (status == MagickFalse)
cristy9bb7c842014-06-17 23:42:24 +0000524 {
525 (void) ThrowMagickException(exception,GetMagickModule(),
526 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
527 return(FALSE);
528 }
cristy3ed852e2009-09-05 21:47:34 +0000529 }
530 if (image->debug != MagickFalse)
531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000532 "Profile: ICC, %.20g bytes",(double) length);
cristydf878012014-01-12 23:39:40 +0000533 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000534}
535
536static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
537{
538 char
539 magick[MaxTextExtent];
540
541 ErrorManager
542 *error_manager;
543
cristy018f07f2011-09-04 21:15:19 +0000544 ExceptionInfo
545 *exception;
546
cristy3ed852e2009-09-05 21:47:34 +0000547 Image
548 *image;
549
550 MagickBooleanType
551 status;
552
cristybb503372010-05-27 20:51:26 +0000553 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000554 i;
555
556 register unsigned char
557 *p;
558
559 size_t
560 length;
561
562 StringInfo
563 *iptc_profile,
564 *profile;
565
566 /*
567 Determine length of binary data stored here.
568 */
cristybb503372010-05-27 20:51:26 +0000569 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000570 length+=(size_t) GetCharacter(jpeg_info);
571 length-=2;
572 if (length <= 14)
573 {
574 while (length-- > 0)
575 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000576 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000577 }
578 /*
579 Validate that this was written as a Photoshop resource format slug.
580 */
581 for (i=0; i < 10; i++)
582 magick[i]=(char) GetCharacter(jpeg_info);
583 magick[10]='\0';
cristy4d0ca342014-05-01 00:42:09 +0000584 length-=10;
cristy3ed852e2009-09-05 21:47:34 +0000585 if (length <= 10)
cristydf878012014-01-12 23:39:40 +0000586 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000587 if (LocaleCompare(magick,"Photoshop ") != 0)
588 {
589 /*
590 Not a IPTC profile, return.
591 */
cristybb503372010-05-27 20:51:26 +0000592 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000593 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000594 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000595 }
596 /*
597 Remove the version number.
598 */
599 for (i=0; i < 4; i++)
600 (void) GetCharacter(jpeg_info);
cristy35892192014-05-26 12:04:36 +0000601 if (length <= 11)
cristy58a749e2014-05-25 17:36:53 +0000602 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000603 length-=4;
cristy3ed852e2009-09-05 21:47:34 +0000604 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000605 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000606 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000607 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000608 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000609 {
610 (void) ThrowMagickException(exception,GetMagickModule(),
611 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
612 return(FALSE);
613 }
cristyf551aee2012-09-27 21:45:23 +0000614 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000615 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000616 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000617 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000618 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000619 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
620 if (iptc_profile != (StringInfo *) NULL)
621 {
622 ConcatenateStringInfo(iptc_profile,profile);
623 profile=DestroyStringInfo(profile);
624 }
625 else
626 {
cristyd15e6592011-10-15 00:13:06 +0000627 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000628 profile=DestroyStringInfo(profile);
629 if (status == MagickFalse)
cristy9bb7c842014-06-17 23:42:24 +0000630 {
631 (void) ThrowMagickException(exception,GetMagickModule(),
632 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
633 return(FALSE);
634 }
cristy3ed852e2009-09-05 21:47:34 +0000635 }
636 if (image->debug != MagickFalse)
637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000638 "Profile: iptc, %.20g bytes",(double) length);
cristydf878012014-01-12 23:39:40 +0000639 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000640}
641
642static boolean ReadProfile(j_decompress_ptr jpeg_info)
643{
644 char
645 name[MaxTextExtent];
646
cristye23ec9d2011-08-16 18:15:40 +0000647 const StringInfo
648 *previous_profile;
649
cristy3ed852e2009-09-05 21:47:34 +0000650 ErrorManager
651 *error_manager;
652
cristy018f07f2011-09-04 21:15:19 +0000653 ExceptionInfo
654 *exception;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 Image
657 *image;
658
659 int
660 marker;
661
662 MagickBooleanType
663 status;
664
cristybb503372010-05-27 20:51:26 +0000665 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000666 i;
667
668 register unsigned char
669 *p;
670
671 size_t
672 length;
673
674 StringInfo
675 *profile;
676
677 /*
678 Read generic profile.
679 */
cristybb503372010-05-27 20:51:26 +0000680 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000681 length+=(size_t) GetCharacter(jpeg_info);
682 if (length <= 2)
cristydf878012014-01-12 23:39:40 +0000683 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000684 length-=2;
685 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000686 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000687 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000688 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000689 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000690 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000691 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000692 {
693 (void) ThrowMagickException(exception,GetMagickModule(),
694 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
695 return(FALSE);
696 }
cristyf551aee2012-09-27 21:45:23 +0000697 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000698 p=GetStringInfoDatum(profile);
cristy08f255a2011-08-17 01:23:06 +0000699 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000700 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000701 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000702 if (marker == 1)
703 {
704 p=GetStringInfoDatum(profile);
705 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
706 (void) CopyMagickString(name,"exif",MaxTextExtent);
707 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
708 {
cristybb503372010-05-27 20:51:26 +0000709 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000710 j;
711
712 /*
713 Extract namespace from XMP profile.
714 */
715 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000716 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000717 {
718 if (*p == '\0')
719 break;
720 p++;
721 }
cristybb503372010-05-27 20:51:26 +0000722 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000723 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
724 (void) CopyMagickString(name,"xmp",MaxTextExtent);
725 }
726 }
cristye23ec9d2011-08-16 18:15:40 +0000727 previous_profile=GetImageProfile(image,name);
728 if (previous_profile != (const StringInfo *) NULL)
cristy08f255a2011-08-17 01:23:06 +0000729 {
cristy1b58f252012-03-01 01:41:41 +0000730 size_t
cristy05c0c9a2011-09-05 23:16:13 +0000731 length;
732
733 length=GetStringInfoLength(profile);
cristy08f255a2011-08-17 01:23:06 +0000734 SetStringInfoLength(profile,GetStringInfoLength(profile)+
735 GetStringInfoLength(previous_profile));
cristy83ab3d82011-09-06 12:05:51 +0000736 (void) memmove(GetStringInfoDatum(profile)+
737 GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
738 length);
cristy08f255a2011-08-17 01:23:06 +0000739 (void) memcpy(GetStringInfoDatum(profile),
740 GetStringInfoDatum(previous_profile),
741 GetStringInfoLength(previous_profile));
742 }
cristyd15e6592011-10-15 00:13:06 +0000743 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000744 profile=DestroyStringInfo(profile);
745 if (status == MagickFalse)
cristydf878012014-01-12 23:39:40 +0000746 {
747 (void) ThrowMagickException(exception,GetMagickModule(),
748 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
749 return(FALSE);
750 }
cristy3ed852e2009-09-05 21:47:34 +0000751 if (image->debug != MagickFalse)
752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000753 "Profile: %s, %.20g bytes",name,(double) length);
cristydf878012014-01-12 23:39:40 +0000754 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000755}
756
cristyf2faecf2010-05-28 19:19:36 +0000757static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000758{
759 SourceManager
760 *source;
761
762 if (number_bytes <= 0)
763 return;
764 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000765 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000766 {
cristyf2faecf2010-05-28 19:19:36 +0000767 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000768 (void) FillInputBuffer(cinfo);
769 }
cristy4cb162a2010-05-30 03:04:47 +0000770 source->manager.next_input_byte+=number_bytes;
771 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000772}
773
774static void TerminateSource(j_decompress_ptr cinfo)
775{
cristydf0d90e2011-12-12 01:03:55 +0000776 (void) cinfo;
cristy3ed852e2009-09-05 21:47:34 +0000777}
778
779static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
780{
781 SourceManager
782 *source;
783
784 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
785 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
786 source=(SourceManager *) cinfo->src;
787 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
788 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
789 source=(SourceManager *) cinfo->src;
790 source->manager.init_source=InitializeSource;
791 source->manager.fill_input_buffer=FillInputBuffer;
792 source->manager.skip_input_data=SkipInputData;
793 source->manager.resync_to_restart=jpeg_resync_to_restart;
794 source->manager.term_source=TerminateSource;
795 source->manager.bytes_in_buffer=0;
796 source->manager.next_input_byte=NULL;
797 source->image=image;
798}
799
800static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
dirk881355a2013-08-19 19:56:50 +0000801 Image *image, ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000802{
803 image->quality=UndefinedCompressionQuality;
804#if defined(D_PROGRESSIVE_SUPPORTED)
805 if (image->compression == LosslessJPEGCompression)
806 {
dirk29dd80e2013-10-31 23:11:11 +0000807 image->quality=100;
cristy3ed852e2009-09-05 21:47:34 +0000808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
809 "Quality: 100 (lossless)");
810 }
811 else
812#endif
813 {
cristybb503372010-05-27 20:51:26 +0000814 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000815 j,
816 qvalue,
817 sum;
818
cristybb503372010-05-27 20:51:26 +0000819 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000820 i;
821
822 /*
823 Determine the JPEG compression quality from the quantization tables.
824 */
825 sum=0;
826 for (i=0; i < NUM_QUANT_TBLS; i++)
827 {
828 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
829 for (j=0; j < DCTSIZE2; j++)
830 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
dirk881355a2013-08-19 19:56:50 +0000831 }
832 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
833 (jpeg_info->quant_tbl_ptrs[1] != NULL))
834 {
835 ssize_t
836 hash[101] =
837 {
838 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
839 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
840 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
841 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
842 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
843 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
844 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
845 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
846 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
847 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
848 0
849 },
850 sums[101] =
851 {
852 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
853 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
854 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
855 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
856 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
857 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
858 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
859 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
860 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
861 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
862 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
863 128, 0
864 };
865
866 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
867 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
868 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
869 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
870 for (i=0; i < 100; i++)
871 {
872 if ((qvalue < hash[i]) && (sum < sums[i]))
873 continue;
874 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000875 image->quality=(size_t) i+1;
dirk881355a2013-08-19 19:56:50 +0000876 if (image->debug != MagickFalse)
877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
878 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
879 (sum <= sums[i]) ? "exact" : "approximate");
880 break;
881 }
882 }
883 else
884 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
885 {
886 ssize_t
887 hash[101] =
888 {
889 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
890 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
891 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
892 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
893 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
894 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
895 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
896 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
897 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
898 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
cristy3ed852e2009-09-05 21:47:34 +0000899 0
dirk881355a2013-08-19 19:56:50 +0000900 },
901 sums[101] =
902 {
903 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
904 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
905 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
906 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
907 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
908 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
909 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
910 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
911 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
912 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
913 667, 592, 518, 441, 369, 292, 221, 151, 86,
914 64, 0
915 };
cristy3ed852e2009-09-05 21:47:34 +0000916
dirk881355a2013-08-19 19:56:50 +0000917 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
918 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
919 for (i=0; i < 100; i++)
920 {
921 if ((qvalue < hash[i]) && (sum < sums[i]))
922 continue;
923 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000924 image->quality=(size_t)i+1;
dirk881355a2013-08-19 19:56:50 +0000925 if (image->debug != MagickFalse)
926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
927 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
928 (sum <= sums[i]) ? "exact" : "approximate");
929 break;
930 }
931 }
cristy3ed852e2009-09-05 21:47:34 +0000932 }
933}
934
cristyd15e6592011-10-15 00:13:06 +0000935static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000936{
937 char
938 sampling_factor[MaxTextExtent];
939
940 switch (jpeg_info->out_color_space)
941 {
942 case JCS_CMYK:
943 {
944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000945 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000946 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
947 jpeg_info->comp_info[0].v_samp_factor,
948 jpeg_info->comp_info[1].h_samp_factor,
949 jpeg_info->comp_info[1].v_samp_factor,
950 jpeg_info->comp_info[2].h_samp_factor,
951 jpeg_info->comp_info[2].v_samp_factor,
952 jpeg_info->comp_info[3].h_samp_factor,
953 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000954 break;
cristy3ed852e2009-09-05 21:47:34 +0000955 }
956 case JCS_GRAYSCALE:
957 {
958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
959 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000960 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000961 jpeg_info->comp_info[0].h_samp_factor,
962 jpeg_info->comp_info[0].v_samp_factor);
963 break;
964 }
965 case JCS_RGB:
966 {
967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000968 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000969 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
970 jpeg_info->comp_info[0].v_samp_factor,
971 jpeg_info->comp_info[1].h_samp_factor,
972 jpeg_info->comp_info[1].v_samp_factor,
973 jpeg_info->comp_info[2].h_samp_factor,
974 jpeg_info->comp_info[2].v_samp_factor);
975 break;
976 }
977 default:
978 {
979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
980 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000981 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000982 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
983 jpeg_info->comp_info[0].v_samp_factor,
984 jpeg_info->comp_info[1].h_samp_factor,
985 jpeg_info->comp_info[1].v_samp_factor,
986 jpeg_info->comp_info[2].h_samp_factor,
987 jpeg_info->comp_info[2].v_samp_factor,
988 jpeg_info->comp_info[3].h_samp_factor,
989 jpeg_info->comp_info[3].v_samp_factor);
990 break;
991 }
992 }
cristyd15e6592011-10-15 00:13:06 +0000993 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
994 exception);
cristye90d7402010-03-14 18:21:29 +0000995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
996 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000997}
998
999static Image *ReadJPEGImage(const ImageInfo *image_info,
1000 ExceptionInfo *exception)
1001{
1002 char
1003 value[MaxTextExtent];
1004
cristycaec74e2009-09-14 02:20:04 +00001005 const char
cristy11151212009-09-14 13:10:15 +00001006 *option;
cristycaec74e2009-09-14 02:20:04 +00001007
cristy3ed852e2009-09-05 21:47:34 +00001008 ErrorManager
1009 error_manager;
1010
cristy3ed852e2009-09-05 21:47:34 +00001011 Image
1012 *image;
1013
cristy3ed852e2009-09-05 21:47:34 +00001014 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00001015 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001016
1017 JSAMPROW
1018 scanline[1];
1019
1020 MagickBooleanType
1021 debug,
1022 status;
1023
1024 MagickSizeType
1025 number_pixels;
1026
cristy22646e22013-06-23 16:34:03 +00001027 MemoryInfo
1028 *memory_info;
1029
cristy4c08aed2011-07-01 19:47:50 +00001030 Quantum
1031 index;
1032
cristybb503372010-05-27 20:51:26 +00001033 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001034 i;
1035
1036 struct jpeg_decompress_struct
1037 jpeg_info;
1038
1039 struct jpeg_error_mgr
1040 jpeg_error;
1041
1042 register JSAMPLE
1043 *p;
1044
cristybb503372010-05-27 20:51:26 +00001045 size_t
cristy3ed852e2009-09-05 21:47:34 +00001046 units;
1047
cristy524222d2011-04-25 00:37:06 +00001048 ssize_t
1049 y;
1050
cristy3ed852e2009-09-05 21:47:34 +00001051 /*
1052 Open image file.
1053 */
1054 assert(image_info != (const ImageInfo *) NULL);
1055 assert(image_info->signature == MagickSignature);
1056 if (image_info->debug != MagickFalse)
1057 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1058 image_info->filename);
1059 assert(exception != (ExceptionInfo *) NULL);
1060 assert(exception->signature == MagickSignature);
1061 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +00001062 (void) debug;
cristy9950d572011-10-01 18:22:35 +00001063 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001064 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1065 if (status == MagickFalse)
1066 {
1067 image=DestroyImageList(image);
1068 return((Image *) NULL);
1069 }
1070 /*
1071 Initialize JPEG parameters.
1072 */
cristy91044972011-04-22 14:21:16 +00001073 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001074 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1075 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1076 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001077 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +00001078 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy72f87f82013-06-23 16:43:05 +00001079 memory_info=(MemoryInfo *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001080 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001081 error_manager.image=image;
1082 if (setjmp(error_manager.error_recovery) != 0)
1083 {
1084 jpeg_destroy_decompress(&jpeg_info);
cristyf551aee2012-09-27 21:45:23 +00001085 if (error_manager.profile != (StringInfo *) NULL)
1086 error_manager.profile=DestroyStringInfo(error_manager.profile);
cristy3ed852e2009-09-05 21:47:34 +00001087 (void) CloseBlob(image);
1088 number_pixels=(MagickSizeType) image->columns*image->rows;
1089 if (number_pixels != 0)
1090 return(GetFirstImageInList(image));
1091 return(DestroyImage(image));
1092 }
1093 jpeg_info.client_data=(void *) &error_manager;
1094 jpeg_create_decompress(&jpeg_info);
1095 JPEGSourceManager(&jpeg_info,image);
1096 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
cristy00db0212014-06-25 23:01:19 +00001097 option=GetImageOption(image_info,"profile:skip");
cristy53b6cc32014-06-25 13:13:33 +00001098 if (IsOptionMember("ICC",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001099 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
cristy53b6cc32014-06-25 13:13:33 +00001100 if (IsOptionMember("IPTC",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001101 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
cristy3ed852e2009-09-05 21:47:34 +00001102 for (i=1; i < 16; i++)
1103 if ((i != 2) && (i != 13) && (i != 14))
cristy53b6cc32014-06-25 13:13:33 +00001104 if (IsOptionMember("APP",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001105 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristydf878012014-01-12 23:39:40 +00001106 i=(ssize_t) jpeg_read_header(&jpeg_info,TRUE);
cristy53215c82009-09-19 16:32:36 +00001107 if ((image_info->colorspace == YCbCrColorspace) ||
1108 (image_info->colorspace == Rec601YCbCrColorspace) ||
1109 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001110 jpeg_info.out_color_space=JCS_YCbCr;
1111 /*
1112 Set image resolution.
1113 */
1114 units=0;
1115 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1116 (jpeg_info.Y_density != 1))
1117 {
cristy2a11bef2011-10-28 18:33:11 +00001118 image->resolution.x=(double) jpeg_info.X_density;
1119 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001120 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001121 }
1122 if (units == 1)
1123 image->units=PixelsPerInchResolution;
1124 if (units == 2)
1125 image->units=PixelsPerCentimeterResolution;
1126 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy092ec8d2013-04-26 13:46:22 +00001127 option=GetImageOption(image_info,"jpeg:size");
cristy7e7d8e92014-10-25 22:24:51 +00001128 if ((option != (const char *) NULL) &&
1129 (jpeg_info.out_color_space != JCS_YCbCr))
cristy3ed852e2009-09-05 21:47:34 +00001130 {
1131 double
1132 scale_factor;
1133
cristycaec74e2009-09-14 02:20:04 +00001134 GeometryInfo
1135 geometry_info;
1136
cristy0adb4f92009-11-28 18:08:51 +00001137 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001138 flags;
1139
cristy3ed852e2009-09-05 21:47:34 +00001140 /*
cristycaec74e2009-09-14 02:20:04 +00001141 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001142 */
cristy11151212009-09-14 13:10:15 +00001143 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001144 if ((flags & SigmaValue) == 0)
1145 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001146 jpeg_calc_output_dimensions(&jpeg_info);
1147 image->magick_columns=jpeg_info.output_width;
1148 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001149 scale_factor=1.0;
1150 if (geometry_info.rho != 0.0)
1151 scale_factor=jpeg_info.output_width/geometry_info.rho;
1152 if ((geometry_info.sigma != 0.0) &&
1153 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1154 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001155 jpeg_info.scale_num=1U;
1156 jpeg_info.scale_denom=(unsigned int) scale_factor;
1157 jpeg_calc_output_dimensions(&jpeg_info);
1158 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1160 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001161 }
cristy3ed852e2009-09-05 21:47:34 +00001162#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1163#if defined(D_LOSSLESS_SUPPORTED)
1164 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1165 JPEGInterlace : NoInterlace;
1166 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1167 LosslessJPEGCompression : JPEGCompression;
1168 if (jpeg_info.data_precision > 8)
1169 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1170 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1171 image->filename);
1172 if (jpeg_info.data_precision == 16)
1173 jpeg_info.data_precision=12;
1174#else
1175 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1176 NoInterlace;
1177 image->compression=JPEGCompression;
1178#endif
1179#else
1180 image->compression=JPEGCompression;
1181 image->interlace=JPEGInterlace;
1182#endif
cristy092ec8d2013-04-26 13:46:22 +00001183 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001184 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001185 {
cristyb4bb39c2012-02-21 18:45:54 +00001186 /*
1187 Let the JPEG library quantize the image.
1188 */
cristydf878012014-01-12 23:39:40 +00001189 jpeg_info.quantize_colors=TRUE;
anthony41906552011-10-07 12:15:27 +00001190 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001191 }
cristy092ec8d2013-04-26 13:46:22 +00001192 option=GetImageOption(image_info,"jpeg:block-smoothing");
cristy0d737182013-04-04 00:22:57 +00001193 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001194 jpeg_info.do_block_smoothing=IsStringTrue(option) != MagickFalse ? TRUE :
1195 FALSE;
cristyb4bb39c2012-02-21 18:45:54 +00001196 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00001197 option=GetImageOption(image_info,"jpeg:dct-method");
cristy787d4352010-03-06 13:55:58 +00001198 if (option != (const char *) NULL)
1199 switch (*option)
1200 {
1201 case 'D':
1202 case 'd':
1203 {
1204 if (LocaleCompare(option,"default") == 0)
1205 jpeg_info.dct_method=JDCT_DEFAULT;
1206 break;
1207 }
1208 case 'F':
1209 case 'f':
1210 {
1211 if (LocaleCompare(option,"fastest") == 0)
1212 jpeg_info.dct_method=JDCT_FASTEST;
1213 if (LocaleCompare(option,"float") == 0)
1214 jpeg_info.dct_method=JDCT_FLOAT;
1215 break;
1216 }
1217 case 'I':
1218 case 'i':
1219 {
1220 if (LocaleCompare(option,"ifast") == 0)
1221 jpeg_info.dct_method=JDCT_IFAST;
1222 if (LocaleCompare(option,"islow") == 0)
1223 jpeg_info.dct_method=JDCT_ISLOW;
1224 break;
1225 }
1226 }
cristy092ec8d2013-04-26 13:46:22 +00001227 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
cristy0d737182013-04-04 00:22:57 +00001228 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001229 jpeg_info.do_fancy_upsampling=IsStringTrue(option) != MagickFalse ? TRUE :
1230 FALSE;
cristy3ed852e2009-09-05 21:47:34 +00001231 (void) jpeg_start_decompress(&jpeg_info);
1232 image->columns=jpeg_info.output_width;
1233 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001234 image->depth=(size_t) jpeg_info.data_precision;
cristy93ff2cc2012-05-13 21:03:53 +00001235 switch (jpeg_info.out_color_space)
1236 {
1237 case JCS_RGB:
1238 default:
1239 {
cristyf551aee2012-09-27 21:45:23 +00001240 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001241 break;
1242 }
1243 case JCS_GRAYSCALE:
1244 {
cristy1a45be72013-04-07 00:16:24 +00001245 (void) SetImageColorspace(image,GRAYColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001246 break;
1247 }
1248 case JCS_YCbCr:
1249 {
cristyf551aee2012-09-27 21:45:23 +00001250 (void) SetImageColorspace(image,YCbCrColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001251 break;
1252 }
1253 case JCS_CMYK:
1254 {
cristyf551aee2012-09-27 21:45:23 +00001255 (void) SetImageColorspace(image,CMYKColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001256 break;
1257 }
1258 }
cristy36fc5502012-05-22 11:32:23 +00001259 if (IsITUFaxImage(image) != MagickFalse)
1260 {
cristyf551aee2012-09-27 21:45:23 +00001261 (void) SetImageColorspace(image,LabColorspace,exception);
cristy36fc5502012-05-22 11:32:23 +00001262 jpeg_info.out_color_space=JCS_YCbCr;
1263 }
cristy092ec8d2013-04-26 13:46:22 +00001264 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001265 if (option != (const char *) NULL)
1266 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1267 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001268 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1269 if ((jpeg_info.output_components == 1) &&
1270 (jpeg_info.quantize_colors == MagickFalse))
1271 {
cristybb503372010-05-27 20:51:26 +00001272 size_t
cristy3ed852e2009-09-05 21:47:34 +00001273 colors;
1274
cristybb503372010-05-27 20:51:26 +00001275 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001276 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001277 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1278 }
1279 if (image->debug != MagickFalse)
1280 {
1281 if (image->interlace != NoInterlace)
1282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1283 "Interlace: progressive");
1284 else
1285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1286 "Interlace: nonprogressive");
1287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1288 (int) jpeg_info.data_precision);
1289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1290 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1291 }
dirk881355a2013-08-19 19:56:50 +00001292 JPEGSetImageQuality(&jpeg_info,image,exception);
cristyd15e6592011-10-15 00:13:06 +00001293 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristyb51dff52011-05-19 16:55:47 +00001294 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001295 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001296 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001297 if (image_info->ping != MagickFalse)
1298 {
1299 jpeg_destroy_decompress(&jpeg_info);
1300 (void) CloseBlob(image);
1301 return(GetFirstImageInList(image));
1302 }
cristyacabb842014-12-14 23:36:33 +00001303 status=SetImageExtent(image,image->columns,image->rows,exception);
1304 if (status == MagickFalse)
cristy457f81d2015-02-01 18:13:23 +00001305 {
1306 jpeg_destroy_decompress(&jpeg_info);
1307 return(DestroyImageList(image));
1308 }
1309 if ((jpeg_info.output_components != 1) &&
1310 (jpeg_info.output_components != 3) && (jpeg_info.output_components != 4))
1311 {
1312 jpeg_destroy_decompress(&jpeg_info);
1313 ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
1314 }
cristy22646e22013-06-23 16:34:03 +00001315 memory_info=AcquireVirtualMemory((size_t) image->columns,
1316 jpeg_info.output_components*sizeof(*jpeg_pixels));
1317 if (memory_info == (MemoryInfo *) NULL)
cristyee1bdaa2013-07-16 16:45:03 +00001318 {
1319 jpeg_destroy_decompress(&jpeg_info);
1320 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1321 }
cristy22646e22013-06-23 16:34:03 +00001322 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001323 /*
1324 Convert JPEG pixels to pixel packets.
1325 */
1326 if (setjmp(error_manager.error_recovery) != 0)
1327 {
cristy22646e22013-06-23 16:34:03 +00001328 if (memory_info != (MemoryInfo *) NULL)
1329 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001330 jpeg_destroy_decompress(&jpeg_info);
1331 (void) CloseBlob(image);
1332 number_pixels=(MagickSizeType) image->columns*image->rows;
1333 if (number_pixels != 0)
1334 return(GetFirstImageInList(image));
1335 return(DestroyImage(image));
1336 }
1337 if (jpeg_info.quantize_colors != MagickFalse)
1338 {
cristybb503372010-05-27 20:51:26 +00001339 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001340 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001341 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001342 {
cristy1b58f252012-03-01 01:41:41 +00001343 image->colormap[i].red=(double) ScaleCharToQuantum(
1344 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001345 image->colormap[i].green=image->colormap[i].red;
1346 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001347 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001348 }
1349 else
cristybb503372010-05-27 20:51:26 +00001350 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001351 {
cristy1b58f252012-03-01 01:41:41 +00001352 image->colormap[i].red=(double) ScaleCharToQuantum(
1353 jpeg_info.colormap[0][i]);
1354 image->colormap[i].green=(double) ScaleCharToQuantum(
1355 jpeg_info.colormap[1][i]);
1356 image->colormap[i].blue=(double) ScaleCharToQuantum(
1357 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001358 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001359 }
1360 }
1361 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001362 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001363 {
cristybb503372010-05-27 20:51:26 +00001364 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001365 x;
1366
cristy4c08aed2011-07-01 19:47:50 +00001367 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001368 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001369
1370 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1371 {
1372 (void) ThrowMagickException(exception,GetMagickModule(),
1373 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1374 continue;
1375 }
1376 p=jpeg_pixels;
1377 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001378 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001379 break;
cristy3ed852e2009-09-05 21:47:34 +00001380 if (jpeg_info.data_precision > 8)
1381 {
cristy5328a4c2013-12-03 11:32:13 +00001382 unsigned short
1383 scale;
1384
cristy477b9fd2013-12-03 16:52:31 +00001385 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy3ed852e2009-09-05 21:47:34 +00001386 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001387 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001388 {
cristybb503372010-05-27 20:51:26 +00001389 size_t
cristy3ed852e2009-09-05 21:47:34 +00001390 pixel;
1391
cristy5328a4c2013-12-03 11:32:13 +00001392 pixel=(size_t) (scale*GETJSAMPLE(*p));
cristyc82a27b2011-10-21 01:07:16 +00001393 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001394 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001395 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001396 p++;
cristyed231572011-07-14 02:18:59 +00001397 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001398 }
1399 else
1400 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001401 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001402 {
cristy217ddd62013-12-06 23:27:09 +00001403 SetPixelRed(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1404 q);
1405 SetPixelGreen(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1406 q);
1407 SetPixelBlue(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1408 q);
cristy4c08aed2011-07-01 19:47:50 +00001409 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001410 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001411 }
1412 else
cristybb503372010-05-27 20:51:26 +00001413 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001414 {
cristy217ddd62013-12-06 23:27:09 +00001415 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(scale*
1416 GETJSAMPLE(*p++)),q);
1417 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(scale*
1418 GETJSAMPLE(*p++)),q);
1419 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(scale*
1420 GETJSAMPLE(*p++)),q);
1421 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(scale*
1422 GETJSAMPLE(*p++)),q);
cristy4c08aed2011-07-01 19:47:50 +00001423 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001424 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001425 }
1426 }
1427 else
1428 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001429 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001430 {
cristyc82a27b2011-10-21 01:07:16 +00001431 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001432 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001433 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001434 p++;
cristyed231572011-07-14 02:18:59 +00001435 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001436 }
1437 else
1438 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001439 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001440 {
cristy4c08aed2011-07-01 19:47:50 +00001441 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1442 GETJSAMPLE(*p++)),q);
1443 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1444 GETJSAMPLE(*p++)),q);
1445 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1446 GETJSAMPLE(*p++)),q);
1447 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001448 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001449 }
1450 else
cristybb503372010-05-27 20:51:26 +00001451 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001452 {
cristy4c08aed2011-07-01 19:47:50 +00001453 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1454 (unsigned char) GETJSAMPLE(*p++)),q);
1455 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1456 (unsigned char) GETJSAMPLE(*p++)),q);
1457 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1458 (unsigned char) GETJSAMPLE(*p++)),q);
1459 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1460 (unsigned char) GETJSAMPLE(*p++)),q);
1461 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001462 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001463 }
1464 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1465 break;
cristy524222d2011-04-25 00:37:06 +00001466 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1467 image->rows);
1468 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001469 {
1470 jpeg_abort_decompress(&jpeg_info);
1471 break;
1472 }
cristy3ed852e2009-09-05 21:47:34 +00001473 }
cristyd28b1dd2011-05-14 20:30:38 +00001474 if (status != MagickFalse)
1475 {
1476 error_manager.finished=MagickTrue;
1477 if (setjmp(error_manager.error_recovery) == 0)
1478 (void) jpeg_finish_decompress(&jpeg_info);
1479 }
cristy3ed852e2009-09-05 21:47:34 +00001480 /*
1481 Free jpeg resources.
1482 */
cristy3ed852e2009-09-05 21:47:34 +00001483 jpeg_destroy_decompress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00001484 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001485 (void) CloseBlob(image);
1486 return(GetFirstImageInList(image));
1487}
1488#endif
dirk29dd80e2013-10-31 23:11:11 +00001489
1490
cristy3ed852e2009-09-05 21:47:34 +00001491/*
1492%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493% %
1494% %
1495% %
1496% R e g i s t e r J P E G I m a g e %
1497% %
1498% %
1499% %
1500%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501%
1502% RegisterJPEGImage() adds properties for the JPEG image format to
1503% the list of supported formats. The properties include the image format
1504% tag, a method to read and/or write the format, whether the format
1505% supports the saving of more than one frame to the same file or blob,
1506% whether the format supports native in-memory I/O, and a brief
1507% description of the format.
1508%
1509% The format of the RegisterJPEGImage method is:
1510%
cristybb503372010-05-27 20:51:26 +00001511% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001512%
1513*/
cristybb503372010-05-27 20:51:26 +00001514ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001515{
1516 char
1517 version[MaxTextExtent];
1518
1519 MagickInfo
1520 *entry;
1521
1522 static const char
cristy7138c592009-09-08 13:58:52 +00001523 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001524
1525 *version='\0';
1526#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001527 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001528#endif
cristyb338b712014-11-27 14:06:34 +00001529 entry=SetMagickInfo("JPE");
cristy8e62a802014-12-27 22:43:35 +00001530#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristyb338b712014-11-27 14:06:34 +00001531 entry->thread_support=NoThreadSupport;
1532#endif
1533#if defined(MAGICKCORE_JPEG_DELEGATE)
1534 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1535 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1536#endif
1537 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001538 entry->flags^=CoderAdjoinFlag;
1539 entry->flags^=CoderUseExtensionFlag;
cristyb338b712014-11-27 14:06:34 +00001540 entry->description=ConstantString(description);
1541 if (*version != '\0')
1542 entry->version=ConstantString(version);
1543 entry->mime_type=ConstantString("image/jpeg");
1544 entry->module=ConstantString("JPEG");
1545 (void) RegisterMagickInfo(entry);
cristy3ed852e2009-09-05 21:47:34 +00001546 entry=SetMagickInfo("JPEG");
cristy8e62a802014-12-27 22:43:35 +00001547#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001548 entry->thread_support=NoThreadSupport;
1549#endif
cristy3ed852e2009-09-05 21:47:34 +00001550#if defined(MAGICKCORE_JPEG_DELEGATE)
1551 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1552 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1553#endif
1554 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001555 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +00001556 entry->description=ConstantString(description);
1557 if (*version != '\0')
1558 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001559 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001560 entry->module=ConstantString("JPEG");
1561 (void) RegisterMagickInfo(entry);
1562 entry=SetMagickInfo("JPG");
cristy8e62a802014-12-27 22:43:35 +00001563#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001564 entry->thread_support=NoThreadSupport;
1565#endif
cristy3ed852e2009-09-05 21:47:34 +00001566#if defined(MAGICKCORE_JPEG_DELEGATE)
1567 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1568 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1569#endif
dirk08e9a112015-02-22 01:51:41 +00001570 entry->flags^=CoderAdjoinFlag;
1571 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001572 entry->description=ConstantString(description);
1573 if (*version != '\0')
1574 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001575 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001576 entry->module=ConstantString("JPEG");
1577 (void) RegisterMagickInfo(entry);
cristya663a4f2015-01-23 12:11:57 +00001578 entry=SetMagickInfo("JPS");
1579#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1580 entry->thread_support=NoThreadSupport;
1581#endif
1582#if defined(MAGICKCORE_JPEG_DELEGATE)
1583 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1584 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1585#endif
dirk08e9a112015-02-22 01:51:41 +00001586 entry->flags^=CoderAdjoinFlag;
1587 entry->flags^=CoderUseExtensionFlag;
cristya663a4f2015-01-23 12:11:57 +00001588 entry->description=ConstantString(description);
1589 if (*version != '\0')
1590 entry->version=ConstantString(version);
1591 entry->mime_type=ConstantString("image/jpeg");
cristy609a4682015-01-23 14:35:34 +00001592 entry->module=ConstantString("JPEG");
cristya663a4f2015-01-23 12:11:57 +00001593 (void) RegisterMagickInfo(entry);
cristy3ed852e2009-09-05 21:47:34 +00001594 entry=SetMagickInfo("PJPEG");
cristy8e62a802014-12-27 22:43:35 +00001595#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001596 entry->thread_support=NoThreadSupport;
1597#endif
cristy3ed852e2009-09-05 21:47:34 +00001598#if defined(MAGICKCORE_JPEG_DELEGATE)
1599 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1600 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1601#endif
dirk08e9a112015-02-22 01:51:41 +00001602 entry->flags^=CoderAdjoinFlag;
1603 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001604 entry->description=ConstantString(description);
1605 if (*version != '\0')
1606 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001607 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001608 entry->module=ConstantString("JPEG");
1609 (void) RegisterMagickInfo(entry);
1610 return(MagickImageCoderSignature);
1611}
dirk29dd80e2013-10-31 23:11:11 +00001612
1613
cristy3ed852e2009-09-05 21:47:34 +00001614/*
1615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616% %
1617% %
1618% %
1619% U n r e g i s t e r J P E G I m a g e %
1620% %
1621% %
1622% %
1623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624%
1625% UnregisterJPEGImage() removes format registrations made by the
1626% JPEG module from the list of supported formats.
1627%
1628% The format of the UnregisterJPEGImage method is:
1629%
1630% UnregisterJPEGImage(void)
1631%
1632*/
1633ModuleExport void UnregisterJPEGImage(void)
1634{
1635 (void) UnregisterMagickInfo("PJPG");
cristya663a4f2015-01-23 12:11:57 +00001636 (void) UnregisterMagickInfo("JPS");
cristy3ed852e2009-09-05 21:47:34 +00001637 (void) UnregisterMagickInfo("JPG");
cristya663a4f2015-01-23 12:11:57 +00001638 (void) UnregisterMagickInfo("JPEG");
cristyb338b712014-11-27 14:06:34 +00001639 (void) UnregisterMagickInfo("JPE");
cristy3ed852e2009-09-05 21:47:34 +00001640}
dirk29dd80e2013-10-31 23:11:11 +00001641
1642
cristy3ed852e2009-09-05 21:47:34 +00001643#if defined(MAGICKCORE_JPEG_DELEGATE)
1644/*
1645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646% %
1647% %
1648% %
1649% W r i t e J P E G I m a g e %
1650% %
1651% %
1652% %
1653%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1654%
1655% WriteJPEGImage() writes a JPEG image file and returns it. It
1656% allocates the memory necessary for the new Image structure and returns a
1657% pointer to the new image.
1658%
1659% The format of the WriteJPEGImage method is:
1660%
cristy91044972011-04-22 14:21:16 +00001661% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001662% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001663%
1664% A description of each parameter follows:
1665%
1666% o image_info: the image info.
1667%
1668% o jpeg_image: The image.
1669%
cristy1e178e72011-08-28 19:44:34 +00001670% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001671%
1672*/
1673
cristy1b58f252012-03-01 01:41:41 +00001674static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1675{
1676 assert(table != (QuantizationTable *) NULL);
1677 if (table->slot != (char *) NULL)
1678 table->slot=DestroyString(table->slot);
1679 if (table->description != (char *) NULL)
1680 table->description=DestroyString(table->description);
1681 if (table->levels != (unsigned int *) NULL)
1682 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1683 table=(QuantizationTable *) RelinquishMagickMemory(table);
1684 return(table);
1685}
1686
cristy3ed852e2009-09-05 21:47:34 +00001687static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1688{
1689 DestinationManager
1690 *destination;
1691
1692 destination=(DestinationManager *) cinfo->dest;
1693 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1694 MaxBufferExtent,destination->buffer);
1695 if (destination->manager.free_in_buffer != MaxBufferExtent)
1696 ERREXIT(cinfo,JERR_FILE_WRITE);
1697 destination->manager.next_output_byte=destination->buffer;
1698 return(TRUE);
1699}
1700
cristy1b58f252012-03-01 01:41:41 +00001701static QuantizationTable *GetQuantizationTable(const char *filename,
1702 const char *slot,ExceptionInfo *exception)
1703{
1704 char
1705 *p,
1706 *xml;
1707
1708 const char
1709 *attribute,
1710 *content;
1711
1712 double
1713 value;
1714
1715 register ssize_t
1716 i;
1717
1718 ssize_t
1719 j;
1720
1721 QuantizationTable
1722 *table;
1723
cristy1fcc8b62012-03-02 17:25:55 +00001724 size_t
1725 length;
1726
cristy1b58f252012-03-01 01:41:41 +00001727 XMLTreeInfo
1728 *description,
1729 *levels,
1730 *quantization_tables,
1731 *table_iterator;
1732
1733 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1734 "Loading quantization tables \"%s\" ...",filename);
1735 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001736 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001737 if (xml == (char *) NULL)
1738 return(table);
1739 quantization_tables=NewXMLTree(xml,exception);
1740 if (quantization_tables == (XMLTreeInfo *) NULL)
1741 {
1742 xml=DestroyString(xml);
1743 return(table);
1744 }
1745 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1746 table_iterator != (XMLTreeInfo *) NULL;
1747 table_iterator=GetNextXMLTreeTag(table_iterator))
1748 {
1749 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1750 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1751 break;
1752 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1753 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1754 break;
1755 }
1756 if (table_iterator == (XMLTreeInfo *) NULL)
1757 {
1758 xml=DestroyString(xml);
1759 return(table);
1760 }
1761 description=GetXMLTreeChild(table_iterator,"description");
1762 if (description == (XMLTreeInfo *) NULL)
1763 {
1764 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1765 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1766 quantization_tables=DestroyXMLTree(quantization_tables);
1767 xml=DestroyString(xml);
1768 return(table);
1769 }
1770 levels=GetXMLTreeChild(table_iterator,"levels");
1771 if (levels == (XMLTreeInfo *) NULL)
1772 {
1773 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1774 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1775 quantization_tables=DestroyXMLTree(quantization_tables);
1776 xml=DestroyString(xml);
1777 return(table);
1778 }
1779 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1780 if (table == (QuantizationTable *) NULL)
1781 ThrowFatalException(ResourceLimitFatalError,
1782 "UnableToAcquireQuantizationTable");
1783 table->slot=(char *) NULL;
1784 table->description=(char *) NULL;
1785 table->levels=(unsigned int *) NULL;
1786 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1787 if (attribute != (char *) NULL)
1788 table->slot=ConstantString(attribute);
1789 content=GetXMLTreeContent(description);
1790 if (content != (char *) NULL)
1791 table->description=ConstantString(content);
1792 attribute=GetXMLTreeAttribute(levels,"width");
1793 if (attribute == (char *) NULL)
1794 {
1795 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1796 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1797 quantization_tables=DestroyXMLTree(quantization_tables);
1798 table=DestroyQuantizationTable(table);
1799 xml=DestroyString(xml);
1800 return(table);
1801 }
1802 table->width=StringToUnsignedLong(attribute);
1803 if (table->width == 0)
1804 {
1805 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1806 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1807 quantization_tables=DestroyXMLTree(quantization_tables);
1808 table=DestroyQuantizationTable(table);
1809 xml=DestroyString(xml);
1810 return(table);
1811 }
1812 attribute=GetXMLTreeAttribute(levels,"height");
1813 if (attribute == (char *) NULL)
1814 {
1815 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1816 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1817 quantization_tables=DestroyXMLTree(quantization_tables);
1818 table=DestroyQuantizationTable(table);
1819 xml=DestroyString(xml);
1820 return(table);
1821 }
1822 table->height=StringToUnsignedLong(attribute);
1823 if (table->height == 0)
1824 {
1825 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1826 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1827 quantization_tables=DestroyXMLTree(quantization_tables);
1828 table=DestroyQuantizationTable(table);
1829 xml=DestroyString(xml);
1830 return(table);
1831 }
1832 attribute=GetXMLTreeAttribute(levels,"divisor");
1833 if (attribute == (char *) NULL)
1834 {
1835 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1836 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1837 quantization_tables=DestroyXMLTree(quantization_tables);
1838 table=DestroyQuantizationTable(table);
1839 xml=DestroyString(xml);
1840 return(table);
1841 }
cristy043f3f32012-03-02 17:37:28 +00001842 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1843 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001844 {
1845 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1846 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1847 quantization_tables=DestroyXMLTree(quantization_tables);
1848 table=DestroyQuantizationTable(table);
1849 xml=DestroyString(xml);
1850 return(table);
1851 }
1852 content=GetXMLTreeContent(levels);
1853 if (content == (char *) NULL)
1854 {
1855 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1856 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1857 quantization_tables=DestroyXMLTree(quantization_tables);
1858 table=DestroyQuantizationTable(table);
1859 xml=DestroyString(xml);
1860 return(table);
1861 }
cristy1fcc8b62012-03-02 17:25:55 +00001862 length=(size_t) table->width*table->height;
1863 if (length < 64)
1864 length=64;
cristy092006b2012-03-02 17:26:02 +00001865 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001866 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001867 if (table->levels == (unsigned int *) NULL)
1868 ThrowFatalException(ResourceLimitFatalError,
1869 "UnableToAcquireQuantizationTable");
1870 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1871 {
cristy043f3f32012-03-02 17:37:28 +00001872 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1873 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001874 while (isspace((int) ((unsigned char) *p)) != 0)
1875 p++;
1876 if (*p == ',')
1877 p++;
1878 content=p;
1879 }
cristy043f3f32012-03-02 17:37:28 +00001880 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001881 (void) value;
1882 if (p != content)
1883 {
1884 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1885 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1886 quantization_tables=DestroyXMLTree(quantization_tables);
1887 table=DestroyQuantizationTable(table);
1888 xml=DestroyString(xml);
1889 return(table);
1890 }
cristy043f3f32012-03-02 17:37:28 +00001891 for (j=i; j < 64; j++)
1892 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001893 quantization_tables=DestroyXMLTree(quantization_tables);
1894 xml=DestroyString(xml);
1895 return(table);
1896}
1897
cristy3ed852e2009-09-05 21:47:34 +00001898static void InitializeDestination(j_compress_ptr cinfo)
1899{
1900 DestinationManager
1901 *destination;
1902
1903 destination=(DestinationManager *) cinfo->dest;
1904 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1905 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1906 destination->manager.next_output_byte=destination->buffer;
1907 destination->manager.free_in_buffer=MaxBufferExtent;
1908}
1909
cristy3ed852e2009-09-05 21:47:34 +00001910static void TerminateDestination(j_compress_ptr cinfo)
1911{
1912 DestinationManager
1913 *destination;
1914
1915 destination=(DestinationManager *) cinfo->dest;
1916 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1917 {
1918 ssize_t
1919 count;
1920
1921 count=WriteBlob(destination->image,MaxBufferExtent-
1922 destination->manager.free_in_buffer,destination->buffer);
1923 if (count != (ssize_t)
1924 (MaxBufferExtent-destination->manager.free_in_buffer))
1925 ERREXIT(cinfo,JERR_FILE_WRITE);
1926 }
1927}
1928
1929static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1930{
1931 const char
1932 *name;
1933
1934 const StringInfo
1935 *profile;
1936
1937 MagickBooleanType
1938 iptc;
1939
cristybb503372010-05-27 20:51:26 +00001940 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001941 i;
1942
1943 size_t
cristy524222d2011-04-25 00:37:06 +00001944 length,
1945 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001946
1947 StringInfo
1948 *custom_profile;
1949
cristy3ed852e2009-09-05 21:47:34 +00001950 /*
1951 Save image profile as a APP marker.
1952 */
1953 iptc=MagickFalse;
1954 custom_profile=AcquireStringInfo(65535L);
1955 ResetImageProfileIterator(image);
1956 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1957 {
cristy109e5572010-09-16 18:38:17 +00001958 register unsigned char
1959 *p;
1960
cristy3ed852e2009-09-05 21:47:34 +00001961 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001962 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001963 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001964 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001965 {
1966 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1967 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1968 (unsigned int) length);
1969 }
1970 if (LocaleCompare(name,"ICC") == 0)
1971 {
1972 register unsigned char
1973 *p;
1974
cristy8ba82ae2013-05-29 14:19:49 +00001975 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001976 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001977 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001978 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001979 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001980 {
1981 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001982 p[12]=(unsigned char) ((i/65519L)+1);
1983 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001984 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001985 length);
1986 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001987 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001988 }
1989 }
1990 if (((LocaleCompare(name,"IPTC") == 0) ||
1991 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1992 {
cristybb503372010-05-27 20:51:26 +00001993 size_t
cristy3ed852e2009-09-05 21:47:34 +00001994 roundup;
1995
1996 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001997 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001998 {
1999 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00002000 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00002001 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
2002 {
2003 (void) memcpy(p,"Photoshop 3.0 ",14);
2004 tag_length=14;
2005 }
2006 else
2007 {
2008 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
2009 tag_length=26;
2010 p[24]=(unsigned char) (length >> 8);
2011 p[25]=(unsigned char) (length & 0xff);
2012 }
2013 p[13]=0x00;
2014 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00002015 if (roundup != 0)
2016 p[length+tag_length]='\0';
2017 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
2018 custom_profile),(unsigned int) (length+tag_length+roundup));
2019 }
2020 }
2021 if (LocaleCompare(name,"XMP") == 0)
2022 {
2023 StringInfo
2024 *xmp_profile;
2025
2026 /*
2027 Add namespace to XMP profile.
2028 */
cristy0615f0e2011-10-12 11:36:46 +00002029 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy30841e62014-05-19 00:45:15 +00002030 if (xmp_profile != (StringInfo *) NULL)
cristy8418c7e2014-05-18 18:38:26 +00002031 {
cristyc08a0aa2014-05-25 23:30:09 +00002032 if (profile != (StringInfo *) NULL)
2033 ConcatenateStringInfo(xmp_profile,profile);
cristy8418c7e2014-05-18 18:38:26 +00002034 GetStringInfoDatum(xmp_profile)[28]='\0';
2035 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
2036 {
2037 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
2038 jpeg_write_marker(jpeg_info,XML_MARKER,
2039 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
2040 }
2041 xmp_profile=DestroyStringInfo(xmp_profile);
2042 }
cristy3ed852e2009-09-05 21:47:34 +00002043 }
cristye8c25f92010-06-03 00:53:06 +00002044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2045 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00002046 name=GetNextImageProfile(image);
2047 }
2048 custom_profile=DestroyStringInfo(custom_profile);
2049}
2050
2051static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
2052{
2053 DestinationManager
2054 *destination;
2055
2056 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
2057 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
2058 destination=(DestinationManager *) cinfo->dest;
2059 destination->manager.init_destination=InitializeDestination;
2060 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2061 destination->manager.term_destination=TerminateDestination;
2062 destination->image=image;
2063}
2064
2065static char **SamplingFactorToList(const char *text)
2066{
2067 char
2068 **textlist;
2069
2070 register char
2071 *q;
2072
2073 register const char
2074 *p;
2075
cristybb503372010-05-27 20:51:26 +00002076 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002077 i;
2078
cristybb503372010-05-27 20:51:26 +00002079 size_t
cristy3ed852e2009-09-05 21:47:34 +00002080 lines;
2081
2082 if (text == (char *) NULL)
2083 return((char **) NULL);
2084 /*
2085 Convert string to an ASCII list.
2086 */
2087 lines=1;
2088 for (p=text; *p != '\0'; p++)
2089 if (*p == ',')
2090 lines++;
2091 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
2092 sizeof(*textlist));
2093 if (textlist == (char **) NULL)
2094 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2095 p=text;
cristybb503372010-05-27 20:51:26 +00002096 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00002097 {
2098 for (q=(char *) p; *q != '\0'; q++)
2099 if (*q == ',')
2100 break;
2101 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
2102 sizeof(*textlist[i]));
2103 if (textlist[i] == (char *) NULL)
2104 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2105 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2106 if (*q == '\r')
2107 q++;
2108 p=q+1;
2109 }
2110 textlist[i]=(char *) NULL;
2111 return(textlist);
2112}
2113
2114static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002115 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002116{
2117 const char
2118 *option,
2119 *sampling_factor,
2120 *value;
2121
2122 ErrorManager
2123 error_manager;
2124
cristy21749e92012-02-18 02:29:21 +00002125 int
cristy7b8ed292013-05-18 01:13:56 +00002126 colorspace,
cristy21749e92012-02-18 02:29:21 +00002127 quality;
2128
cristy3ed852e2009-09-05 21:47:34 +00002129 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002130 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002131
2132 JSAMPROW
2133 scanline[1];
2134
cristy3ed852e2009-09-05 21:47:34 +00002135 MagickBooleanType
2136 status;
2137
cristy22646e22013-06-23 16:34:03 +00002138 MemoryInfo
2139 *memory_info;
2140
cristy3ed852e2009-09-05 21:47:34 +00002141 register JSAMPLE
2142 *q;
2143
cristybb503372010-05-27 20:51:26 +00002144 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002145 i;
2146
cristy524222d2011-04-25 00:37:06 +00002147 ssize_t
2148 y;
2149
cristy3ed852e2009-09-05 21:47:34 +00002150 struct jpeg_compress_struct
2151 jpeg_info;
2152
2153 struct jpeg_error_mgr
2154 jpeg_error;
2155
cristy5328a4c2013-12-03 11:32:13 +00002156 unsigned short
2157 scale;
2158
cristy3ed852e2009-09-05 21:47:34 +00002159 /*
2160 Open image file.
2161 */
2162 assert(image_info != (const ImageInfo *) NULL);
2163 assert(image_info->signature == MagickSignature);
2164 assert(image != (Image *) NULL);
2165 assert(image->signature == MagickSignature);
2166 if (image->debug != MagickFalse)
2167 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002168 assert(exception != (ExceptionInfo *) NULL);
2169 assert(exception->signature == MagickSignature);
cristya663a4f2015-01-23 12:11:57 +00002170 if ((LocaleCompare(image_info->magick,"JPS") == 0) &&
2171 (image->next != (Image *) NULL))
2172 image=AppendImages(image,MagickFalse,exception);
cristy73ef4eb2015-01-23 14:31:57 +00002173 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2174 if (status == MagickFalse)
2175 return(status);
cristy3ed852e2009-09-05 21:47:34 +00002176 /*
2177 Initialize JPEG parameters.
2178 */
cristy91044972011-04-22 14:21:16 +00002179 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002180 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2181 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2182 jpeg_info.client_data=(void *) image;
2183 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002184 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002185 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002186 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002187 error_manager.image=image;
cristy72f87f82013-06-23 16:43:05 +00002188 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002189 if (setjmp(error_manager.error_recovery) != 0)
2190 {
2191 jpeg_destroy_compress(&jpeg_info);
2192 (void) CloseBlob(image);
2193 return(MagickFalse);
2194 }
2195 jpeg_info.client_data=(void *) &error_manager;
2196 jpeg_create_compress(&jpeg_info);
2197 JPEGDestinationManager(&jpeg_info,image);
2198 if ((image->columns != (unsigned int) image->columns) ||
2199 (image->rows != (unsigned int) image->rows))
2200 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2201 jpeg_info.image_width=(unsigned int) image->columns;
2202 jpeg_info.image_height=(unsigned int) image->rows;
2203 jpeg_info.input_components=3;
2204 jpeg_info.data_precision=8;
2205 jpeg_info.in_color_space=JCS_RGB;
2206 switch (image->colorspace)
2207 {
2208 case CMYKColorspace:
2209 {
2210 jpeg_info.input_components=4;
2211 jpeg_info.in_color_space=JCS_CMYK;
2212 break;
2213 }
2214 case YCbCrColorspace:
2215 case Rec601YCbCrColorspace:
2216 case Rec709YCbCrColorspace:
2217 {
2218 jpeg_info.in_color_space=JCS_YCbCr;
2219 break;
2220 }
2221 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002222 {
cristye9355552014-01-16 13:35:05 +00002223 if (image_info->type == TrueColorType)
2224 break;
cristy3ed852e2009-09-05 21:47:34 +00002225 jpeg_info.input_components=1;
2226 jpeg_info.in_color_space=JCS_GRAYSCALE;
2227 break;
2228 }
2229 default:
cristy9f396782009-12-21 01:37:45 +00002230 {
cristyaf8d3912014-02-21 14:50:33 +00002231 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye9355552014-01-16 13:35:05 +00002232 if (image_info->type == TrueColorType)
2233 break;
cristyc47ba2d2014-01-16 19:41:18 +00002234 if (IsImageGray(image,exception) != MagickFalse)
cristye9355552014-01-16 13:35:05 +00002235 {
2236 jpeg_info.input_components=1;
2237 jpeg_info.in_color_space=JCS_GRAYSCALE;
2238 }
cristy3ed852e2009-09-05 21:47:34 +00002239 break;
cristy9f396782009-12-21 01:37:45 +00002240 }
cristy3ed852e2009-09-05 21:47:34 +00002241 }
cristy3ed852e2009-09-05 21:47:34 +00002242 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002243 if (jpeg_info.in_color_space == JCS_CMYK)
2244 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002245 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2246 jpeg_info.data_precision=8;
2247 else
dirk93b02b72013-11-16 16:03:36 +00002248 jpeg_info.data_precision=BITS_IN_JSAMPLE;
cristy4d30a482014-10-19 12:58:39 +00002249 jpeg_info.density_unit=1;
cristy3ed852e2009-09-05 21:47:34 +00002250 if (image->debug != MagickFalse)
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002252 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2253 floor(image->resolution.y+0.5));
2254 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002255 {
2256 /*
2257 Set image resolution.
2258 */
cristydf878012014-01-12 23:39:40 +00002259 jpeg_info.write_JFIF_header=TRUE;
cristy2a11bef2011-10-28 18:33:11 +00002260 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2261 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy0c220632013-09-25 22:10:28 +00002262 /*
2263 Set image resolution units.
2264 */
2265 jpeg_info.density_unit=(UINT8) 0;
cristy3ed852e2009-09-05 21:47:34 +00002266 if (image->units == PixelsPerInchResolution)
2267 jpeg_info.density_unit=(UINT8) 1;
2268 if (image->units == PixelsPerCentimeterResolution)
2269 jpeg_info.density_unit=(UINT8) 2;
2270 }
cristy97cb3bf2012-02-21 18:31:20 +00002271 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002272 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002273 if (option != (const char *) NULL)
2274 switch (*option)
2275 {
2276 case 'D':
2277 case 'd':
2278 {
2279 if (LocaleCompare(option,"default") == 0)
2280 jpeg_info.dct_method=JDCT_DEFAULT;
2281 break;
2282 }
2283 case 'F':
2284 case 'f':
2285 {
2286 if (LocaleCompare(option,"fastest") == 0)
2287 jpeg_info.dct_method=JDCT_FASTEST;
2288 if (LocaleCompare(option,"float") == 0)
2289 jpeg_info.dct_method=JDCT_FLOAT;
2290 break;
2291 }
2292 case 'I':
2293 case 'i':
2294 {
2295 if (LocaleCompare(option,"ifast") == 0)
2296 jpeg_info.dct_method=JDCT_IFAST;
2297 if (LocaleCompare(option,"islow") == 0)
2298 jpeg_info.dct_method=JDCT_ISLOW;
2299 break;
2300 }
2301 }
cristy092ec8d2013-04-26 13:46:22 +00002302 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002303 if (option != (const char *) NULL)
cristyb9d0e7e2014-11-02 18:14:44 +00002304 jpeg_info.optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE :
2305 FALSE;
cristy7d31f6e2014-11-11 13:07:58 +00002306 else
2307 {
2308 MagickSizeType
2309 length;
2310
2311 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2312 image->rows*sizeof(JSAMPLE);
2313 if (length == (MagickSizeType) ((size_t) length))
2314 {
2315 /*
2316 Perform optimization only if available memory resources permit it.
2317 */
2318 status=AcquireMagickResource(MemoryResource,length);
2319 RelinquishMagickResource(MemoryResource,length);
2320 jpeg_info.optimize_coding=status == MagickFalse ? FALSE : TRUE;
2321 }
2322 }
cristy3ed852e2009-09-05 21:47:34 +00002323#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2324 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2325 (image_info->interlace != NoInterlace))
2326 {
2327 if (image->debug != MagickFalse)
2328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2329 "Interlace: progressive");
2330 jpeg_simple_progression(&jpeg_info);
2331 }
2332 else
2333 if (image->debug != MagickFalse)
2334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2335 "Interlace: non-progressive");
2336#else
2337 if (image->debug != MagickFalse)
2338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2339 "Interlace: nonprogressive");
2340#endif
dirk871abab2013-11-03 13:21:16 +00002341 quality=92;
cristy2a950402014-03-12 17:57:14 +00002342 if ((image_info->compression != LosslessJPEGCompression) &&
2343 (image->quality <= 100))
2344 {
2345 if (image->quality != UndefinedCompressionQuality)
2346 quality=(int) image->quality;
2347 if (image->debug != MagickFalse)
2348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2349 (double) image->quality);
2350 }
2351 else
2352 {
2353#if !defined(C_LOSSLESS_SUPPORTED)
2354 quality=100;
2355 if (image->debug != MagickFalse)
2356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2357#else
2358 if (image->quality < 100)
2359 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2360 "LosslessToLossyJPEGConversion",image->filename);
2361 else
2362 {
2363 int
2364 point_transform,
2365 predictor;
2366
2367 predictor=image->quality/100; /* range 1-7 */
2368 point_transform=image->quality % 20; /* range 0-15 */
2369 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2370 if (image->debug != MagickFalse)
2371 {
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 "Compression: lossless");
2374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2375 "Predictor: %d",predictor);
2376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2377 "Point Transform: %d",point_transform);
2378 }
2379 }
2380#endif
2381 }
cristy092ec8d2013-04-26 13:46:22 +00002382 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002383 if (option != (const char *) NULL)
2384 {
2385 Image
2386 *jpeg_image;
2387
2388 ImageInfo
2389 *jpeg_info;
2390
2391 jpeg_info=CloneImageInfo(image_info);
cristye0823d52013-11-03 21:50:10 +00002392 jpeg_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002393 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002394 if (jpeg_image != (Image *) NULL)
2395 {
2396 MagickSizeType
2397 extent;
2398
2399 size_t
cristy87e73ab2010-02-12 01:59:12 +00002400 maximum,
2401 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002402
2403 /*
2404 Search for compression quality that does not exceed image extent.
2405 */
2406 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002407 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002408 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002409 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
cristy4e8b2ed2015-03-06 19:54:18 +00002410 maximum=image->quality;
2411 if (maximum < 2)
2412 maximum=2;
dirkcf845242013-08-11 21:36:19 +00002413 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002414 {
cristy3975f402014-02-26 14:36:33 +00002415 (void) AcquireUniqueFilename(jpeg_image->filename);
2416 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
cristy1e178e72011-08-28 19:44:34 +00002417 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002418 if (GetBlobSize(jpeg_image) <= extent)
cristy3975f402014-02-26 14:36:33 +00002419 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002420 else
cristy3975f402014-02-26 14:36:33 +00002421 maximum=jpeg_image->quality-1;
2422 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristy0adb4f92009-11-28 18:08:51 +00002423 }
cristy58d4fe12015-02-07 11:53:05 +00002424 quality=(int) minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002425 jpeg_image=DestroyImage(jpeg_image);
2426 }
2427 jpeg_info=DestroyImageInfo(jpeg_info);
2428 }
cristy9bb7c842014-06-17 23:42:24 +00002429 jpeg_set_quality(&jpeg_info,quality,TRUE);
cristy16e74722012-07-01 13:01:38 +00002430#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002431 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002432 if (option != (const char *) NULL)
2433 {
2434 GeometryInfo
2435 geometry_info;
2436
2437 int
2438 flags;
2439
2440 /*
2441 Set quality scaling for luminance and chrominance separately.
2442 */
2443 flags=ParseGeometry(option,&geometry_info);
2444 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2445 {
2446 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2447 (geometry_info.rho+0.5));
2448 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2449 (geometry_info.sigma+0.5));
cristy9bb7c842014-06-17 23:42:24 +00002450 jpeg_default_qtables(&jpeg_info,TRUE);
cristy16e74722012-07-01 13:01:38 +00002451 }
2452 }
2453#endif
cristy7b8ed292013-05-18 01:13:56 +00002454 colorspace=jpeg_info.in_color_space;
2455 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002456 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002457 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002458 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002459 colorspace=StringToInteger(value);
2460 sampling_factor=(const char *) NULL;
2461 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002462 {
cristy7b8ed292013-05-18 01:13:56 +00002463 value=GetImageOption(image_info,"jpeg:sampling-factor");
2464 if (value == (char *) NULL)
2465 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2466 if (value != (char *) NULL)
2467 {
2468 sampling_factor=value;
2469 if (image->debug != MagickFalse)
2470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2471 " Input sampling-factors=%s",sampling_factor);
2472 }
cristy3ed852e2009-09-05 21:47:34 +00002473 }
cristy7b8ed292013-05-18 01:13:56 +00002474 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002475 if (image_info->sampling_factor != (char *) NULL)
2476 sampling_factor=image_info->sampling_factor;
2477 if (sampling_factor == (const char *) NULL)
2478 {
cristyc243f622015-02-21 17:20:34 +00002479 if (quality >= 90)
cristy3ed852e2009-09-05 21:47:34 +00002480 for (i=0; i < MAX_COMPONENTS; i++)
2481 {
2482 jpeg_info.comp_info[i].h_samp_factor=1;
2483 jpeg_info.comp_info[i].v_samp_factor=1;
2484 }
2485 }
2486 else
2487 {
2488 char
2489 **factors;
2490
2491 GeometryInfo
2492 geometry_info;
2493
2494 MagickStatusType
2495 flags;
2496
2497 /*
2498 Set sampling factor.
2499 */
2500 i=0;
2501 factors=SamplingFactorToList(sampling_factor);
2502 if (factors != (char **) NULL)
2503 {
2504 for (i=0; i < MAX_COMPONENTS; i++)
2505 {
2506 if (factors[i] == (char *) NULL)
2507 break;
2508 flags=ParseGeometry(factors[i],&geometry_info);
2509 if ((flags & SigmaValue) == 0)
2510 geometry_info.sigma=geometry_info.rho;
2511 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2512 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2513 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2514 }
2515 factors=(char **) RelinquishMagickMemory(factors);
2516 }
2517 for ( ; i < MAX_COMPONENTS; i++)
2518 {
2519 jpeg_info.comp_info[i].h_samp_factor=1;
2520 jpeg_info.comp_info[i].v_samp_factor=1;
2521 }
2522 }
cristy092ec8d2013-04-26 13:46:22 +00002523 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002524 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002525 {
cristy1b58f252012-03-01 01:41:41 +00002526 QuantizationTable
2527 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002528
cristy1445b322012-02-19 18:57:30 +00002529 /*
cristy1b58f252012-03-01 01:41:41 +00002530 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002531 */
cristy1b58f252012-03-01 01:41:41 +00002532 table=GetQuantizationTable(option,"0",exception);
2533 if (table != (QuantizationTable *) NULL)
2534 {
cristyae453b72013-04-21 22:24:20 +00002535 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002536 jpeg_info.comp_info[i].quant_tbl_no=0;
2537 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2538 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002539 table=DestroyQuantizationTable(table);
2540 }
2541 table=GetQuantizationTable(option,"1",exception);
2542 if (table != (QuantizationTable *) NULL)
2543 {
cristyae453b72013-04-21 22:24:20 +00002544 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002545 jpeg_info.comp_info[i].quant_tbl_no=1;
2546 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2547 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002548 table=DestroyQuantizationTable(table);
2549 }
2550 table=GetQuantizationTable(option,"2",exception);
2551 if (table != (QuantizationTable *) NULL)
2552 {
cristyae453b72013-04-21 22:24:20 +00002553 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002554 jpeg_info.comp_info[i].quant_tbl_no=2;
2555 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2556 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002557 table=DestroyQuantizationTable(table);
2558 }
2559 table=GetQuantizationTable(option,"3",exception);
2560 if (table != (QuantizationTable *) NULL)
2561 {
cristyae453b72013-04-21 22:24:20 +00002562 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002563 jpeg_info.comp_info[i].quant_tbl_no=3;
2564 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2565 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002566 table=DestroyQuantizationTable(table);
2567 }
cristy1445b322012-02-19 18:57:30 +00002568 }
cristy9bb7c842014-06-17 23:42:24 +00002569 jpeg_start_compress(&jpeg_info,TRUE);
cristy3ed852e2009-09-05 21:47:34 +00002570 if (image->debug != MagickFalse)
2571 {
2572 if (image->storage_class == PseudoClass)
2573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2574 "Storage class: PseudoClass");
2575 else
2576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2577 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2579 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002580 if (image->colors != 0)
2581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002582 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002583 else
2584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2585 "Number of colors: unspecified");
2586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2587 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2588 switch (image->colorspace)
2589 {
2590 case CMYKColorspace:
2591 {
2592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593 "Storage class: DirectClass");
2594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2595 "Colorspace: CMYK");
2596 break;
2597 }
2598 case YCbCrColorspace:
2599 case Rec601YCbCrColorspace:
2600 case Rec709YCbCrColorspace:
2601 {
2602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2603 "Colorspace: YCbCr");
2604 break;
2605 }
2606 default:
2607 break;
2608 }
2609 switch (image->colorspace)
2610 {
2611 case CMYKColorspace:
2612 {
2613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2614 "Colorspace: CMYK");
2615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2616 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2617 jpeg_info.comp_info[0].h_samp_factor,
2618 jpeg_info.comp_info[0].v_samp_factor,
2619 jpeg_info.comp_info[1].h_samp_factor,
2620 jpeg_info.comp_info[1].v_samp_factor,
2621 jpeg_info.comp_info[2].h_samp_factor,
2622 jpeg_info.comp_info[2].v_samp_factor,
2623 jpeg_info.comp_info[3].h_samp_factor,
2624 jpeg_info.comp_info[3].v_samp_factor);
2625 break;
2626 }
2627 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002628 {
2629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2630 "Colorspace: GRAY");
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2633 jpeg_info.comp_info[0].v_samp_factor);
2634 break;
2635 }
cristyed8d7852013-08-01 22:44:22 +00002636 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002637 case RGBColorspace:
2638 {
2639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002640 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2642 "Sampling factors: %dx%d,%dx%d,%dx%d",
2643 jpeg_info.comp_info[0].h_samp_factor,
2644 jpeg_info.comp_info[0].v_samp_factor,
2645 jpeg_info.comp_info[1].h_samp_factor,
2646 jpeg_info.comp_info[1].v_samp_factor,
2647 jpeg_info.comp_info[2].h_samp_factor,
2648 jpeg_info.comp_info[2].v_samp_factor);
2649 break;
2650 }
2651 case YCbCrColorspace:
2652 case Rec601YCbCrColorspace:
2653 case Rec709YCbCrColorspace:
2654 {
2655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2656 "Colorspace: YCbCr");
2657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2658 "Sampling factors: %dx%d,%dx%d,%dx%d",
2659 jpeg_info.comp_info[0].h_samp_factor,
2660 jpeg_info.comp_info[0].v_samp_factor,
2661 jpeg_info.comp_info[1].h_samp_factor,
2662 jpeg_info.comp_info[1].v_samp_factor,
2663 jpeg_info.comp_info[2].h_samp_factor,
2664 jpeg_info.comp_info[2].v_samp_factor);
2665 break;
2666 }
2667 default:
2668 {
2669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2670 image->colorspace);
2671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2672 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2673 jpeg_info.comp_info[0].h_samp_factor,
2674 jpeg_info.comp_info[0].v_samp_factor,
2675 jpeg_info.comp_info[1].h_samp_factor,
2676 jpeg_info.comp_info[1].v_samp_factor,
2677 jpeg_info.comp_info[2].h_samp_factor,
2678 jpeg_info.comp_info[2].v_samp_factor,
2679 jpeg_info.comp_info[3].h_samp_factor,
2680 jpeg_info.comp_info[3].v_samp_factor);
2681 break;
2682 }
2683 }
2684 }
2685 /*
2686 Write JPEG profiles.
2687 */
cristyd15e6592011-10-15 00:13:06 +00002688 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002689 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002690 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002691 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2692 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2693 if (image->profiles != (void *) NULL)
2694 WriteProfile(&jpeg_info,image);
2695 /*
2696 Convert MIFF to JPEG raster pixels.
2697 */
cristy22646e22013-06-23 16:34:03 +00002698 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002699 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002700 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002701 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002702 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002703 if (setjmp(error_manager.error_recovery) != 0)
2704 {
2705 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002706 if (memory_info != (MemoryInfo *) NULL)
2707 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002708 (void) CloseBlob(image);
2709 return(MagickFalse);
2710 }
2711 scanline[0]=(JSAMPROW) jpeg_pixels;
cristy477b9fd2013-12-03 16:52:31 +00002712 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy31db6c12013-12-04 15:17:30 +00002713 if (scale == 0)
2714 scale=1;
cristye90d7402010-03-14 18:21:29 +00002715 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002716 {
cristy3ed852e2009-09-05 21:47:34 +00002717 if ((jpeg_info.in_color_space == JCS_RGB) ||
2718 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002719 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002720 {
cristy4c08aed2011-07-01 19:47:50 +00002721 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002722 *p;
2723
cristybb503372010-05-27 20:51:26 +00002724 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002725 x;
2726
cristy1e178e72011-08-28 19:44:34 +00002727 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002728 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002729 break;
2730 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002731 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002732 {
cristy4c08aed2011-07-01 19:47:50 +00002733 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2734 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2735 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002736 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002737 }
2738 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002739 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2740 image->rows);
2741 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002742 break;
2743 }
2744 else
cristye90d7402010-03-14 18:21:29 +00002745 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002746 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002747 {
cristyd0323222013-04-07 16:13:21 +00002748 register const Quantum
2749 *p;
2750
2751 register ssize_t
2752 x;
2753
2754 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2755 if (p == (const Quantum *) NULL)
2756 break;
2757 q=jpeg_pixels;
2758 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002759 {
cristyd0323222013-04-07 16:13:21 +00002760 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2761 image,p)));
2762 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002763 }
cristyd0323222013-04-07 16:13:21 +00002764 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2765 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2766 image->rows);
2767 if (status == MagickFalse)
2768 break;
2769 }
cristye90d7402010-03-14 18:21:29 +00002770 else
cristybb503372010-05-27 20:51:26 +00002771 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002772 {
cristy4c08aed2011-07-01 19:47:50 +00002773 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002774 *p;
2775
cristybb503372010-05-27 20:51:26 +00002776 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002777 x;
2778
cristy1e178e72011-08-28 19:44:34 +00002779 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002780 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002781 break;
2782 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002783 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002784 {
2785 /*
2786 Convert DirectClass packets to contiguous CMYK scanlines.
2787 */
2788 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002789 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002790 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002791 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002792 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002793 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002794 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002795 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002796 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002797 }
2798 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002799 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2800 image->rows);
2801 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002802 break;
2803 }
2804 }
2805 else
2806 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002807 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002808 {
cristy4c08aed2011-07-01 19:47:50 +00002809 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002810 *p;
2811
cristybb503372010-05-27 20:51:26 +00002812 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002813 x;
2814
cristy1e178e72011-08-28 19:44:34 +00002815 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002816 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002817 break;
2818 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002819 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002820 {
cristy217ddd62013-12-06 23:27:09 +00002821 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2822 p)))/scale);
cristyed231572011-07-14 02:18:59 +00002823 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002824 }
2825 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002826 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2827 image->rows);
2828 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002829 break;
2830 }
2831 else
2832 if ((jpeg_info.in_color_space == JCS_RGB) ||
2833 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002834 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002835 {
cristy4c08aed2011-07-01 19:47:50 +00002836 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002837 *p;
2838
cristybb503372010-05-27 20:51:26 +00002839 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002840 x;
2841
cristy1e178e72011-08-28 19:44:34 +00002842 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002843 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002844 break;
2845 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002846 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002847 {
cristy5328a4c2013-12-03 11:32:13 +00002848 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2849 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2850 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002851 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002852 }
2853 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002854 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002855 image->rows);
cristye90d7402010-03-14 18:21:29 +00002856 if (status == MagickFalse)
2857 break;
2858 }
2859 else
cristybb503372010-05-27 20:51:26 +00002860 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002861 {
cristy4c08aed2011-07-01 19:47:50 +00002862 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002863 *p;
2864
cristybb503372010-05-27 20:51:26 +00002865 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002866 x;
2867
cristy1e178e72011-08-28 19:44:34 +00002868 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002869 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002870 break;
2871 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002872 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002873 {
2874 /*
2875 Convert DirectClass packets to contiguous CMYK scanlines.
2876 */
cristy217ddd62013-12-06 23:27:09 +00002877 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2878 image,p))/scale);
2879 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2880 image,p))/scale);
2881 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2882 image,p))/scale);
2883 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2884 image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002885 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002886 }
2887 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002888 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002889 image->rows);
cristye90d7402010-03-14 18:21:29 +00002890 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002891 break;
2892 }
cristybb503372010-05-27 20:51:26 +00002893 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002894 jpeg_finish_compress(&jpeg_info);
2895 /*
2896 Relinquish resources.
2897 */
2898 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002899 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002900 (void) CloseBlob(image);
2901 return(MagickTrue);
2902}
2903#endif