blob: cab17ad2b8e4a94fe19842571c7fb6d4958a43e5 [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{
dirk06b627a2015-04-06 18:59:17 +00001516#define JPEGDescription "Joint Photographic Experts Group JFIF format"
1517
cristy3ed852e2009-09-05 21:47:34 +00001518 char
1519 version[MaxTextExtent];
1520
1521 MagickInfo
1522 *entry;
1523
cristy3ed852e2009-09-05 21:47:34 +00001524 *version='\0';
1525#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001526 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001527#endif
dirk06b627a2015-04-06 18:59:17 +00001528 entry=AcquireMagickInfo("JPEG","JPE",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001529#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristyb338b712014-11-27 14:06:34 +00001530 entry->thread_support=NoThreadSupport;
1531#endif
1532#if defined(MAGICKCORE_JPEG_DELEGATE)
1533 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1534 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1535#endif
1536 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001537 entry->flags^=CoderAdjoinFlag;
1538 entry->flags^=CoderUseExtensionFlag;
cristyb338b712014-11-27 14:06:34 +00001539 if (*version != '\0')
1540 entry->version=ConstantString(version);
1541 entry->mime_type=ConstantString("image/jpeg");
cristyb338b712014-11-27 14:06:34 +00001542 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001543 entry=AcquireMagickInfo("JPEG","JPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001544#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001545 entry->thread_support=NoThreadSupport;
1546#endif
cristy3ed852e2009-09-05 21:47:34 +00001547#if defined(MAGICKCORE_JPEG_DELEGATE)
1548 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1549 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1550#endif
1551 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001552 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +00001553 if (*version != '\0')
1554 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001555 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001556 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001557 entry=AcquireMagickInfo("JPEG","JPG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001558#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001559 entry->thread_support=NoThreadSupport;
1560#endif
cristy3ed852e2009-09-05 21:47:34 +00001561#if defined(MAGICKCORE_JPEG_DELEGATE)
1562 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1563 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1564#endif
dirk08e9a112015-02-22 01:51:41 +00001565 entry->flags^=CoderAdjoinFlag;
1566 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001567 if (*version != '\0')
1568 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001569 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001570 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001571 entry=AcquireMagickInfo("JPEG","JPS",JPEGDescription);
cristya663a4f2015-01-23 12:11:57 +00001572#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1573 entry->thread_support=NoThreadSupport;
1574#endif
1575#if defined(MAGICKCORE_JPEG_DELEGATE)
1576 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1577 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1578#endif
dirk08e9a112015-02-22 01:51:41 +00001579 entry->flags^=CoderAdjoinFlag;
1580 entry->flags^=CoderUseExtensionFlag;
cristya663a4f2015-01-23 12:11:57 +00001581 if (*version != '\0')
1582 entry->version=ConstantString(version);
1583 entry->mime_type=ConstantString("image/jpeg");
cristya663a4f2015-01-23 12:11:57 +00001584 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001585 entry=AcquireMagickInfo("JPEG","PJPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001586#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001587 entry->thread_support=NoThreadSupport;
1588#endif
cristy3ed852e2009-09-05 21:47:34 +00001589#if defined(MAGICKCORE_JPEG_DELEGATE)
1590 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1591 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1592#endif
dirk08e9a112015-02-22 01:51:41 +00001593 entry->flags^=CoderAdjoinFlag;
1594 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001595 if (*version != '\0')
1596 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001597 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001598 (void) RegisterMagickInfo(entry);
1599 return(MagickImageCoderSignature);
1600}
dirk29dd80e2013-10-31 23:11:11 +00001601
1602
cristy3ed852e2009-09-05 21:47:34 +00001603/*
1604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605% %
1606% %
1607% %
1608% U n r e g i s t e r J P E G I m a g e %
1609% %
1610% %
1611% %
1612%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1613%
1614% UnregisterJPEGImage() removes format registrations made by the
1615% JPEG module from the list of supported formats.
1616%
1617% The format of the UnregisterJPEGImage method is:
1618%
1619% UnregisterJPEGImage(void)
1620%
1621*/
1622ModuleExport void UnregisterJPEGImage(void)
1623{
1624 (void) UnregisterMagickInfo("PJPG");
cristya663a4f2015-01-23 12:11:57 +00001625 (void) UnregisterMagickInfo("JPS");
cristy3ed852e2009-09-05 21:47:34 +00001626 (void) UnregisterMagickInfo("JPG");
cristya663a4f2015-01-23 12:11:57 +00001627 (void) UnregisterMagickInfo("JPEG");
cristyb338b712014-11-27 14:06:34 +00001628 (void) UnregisterMagickInfo("JPE");
cristy3ed852e2009-09-05 21:47:34 +00001629}
dirk29dd80e2013-10-31 23:11:11 +00001630
1631
cristy3ed852e2009-09-05 21:47:34 +00001632#if defined(MAGICKCORE_JPEG_DELEGATE)
1633/*
1634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1635% %
1636% %
1637% %
1638% W r i t e J P E G I m a g e %
1639% %
1640% %
1641% %
1642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1643%
1644% WriteJPEGImage() writes a JPEG image file and returns it. It
1645% allocates the memory necessary for the new Image structure and returns a
1646% pointer to the new image.
1647%
1648% The format of the WriteJPEGImage method is:
1649%
cristy91044972011-04-22 14:21:16 +00001650% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001651% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001652%
1653% A description of each parameter follows:
1654%
1655% o image_info: the image info.
1656%
1657% o jpeg_image: The image.
1658%
cristy1e178e72011-08-28 19:44:34 +00001659% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001660%
1661*/
1662
cristy1b58f252012-03-01 01:41:41 +00001663static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1664{
1665 assert(table != (QuantizationTable *) NULL);
1666 if (table->slot != (char *) NULL)
1667 table->slot=DestroyString(table->slot);
1668 if (table->description != (char *) NULL)
1669 table->description=DestroyString(table->description);
1670 if (table->levels != (unsigned int *) NULL)
1671 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1672 table=(QuantizationTable *) RelinquishMagickMemory(table);
1673 return(table);
1674}
1675
cristy3ed852e2009-09-05 21:47:34 +00001676static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1677{
1678 DestinationManager
1679 *destination;
1680
1681 destination=(DestinationManager *) cinfo->dest;
1682 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1683 MaxBufferExtent,destination->buffer);
1684 if (destination->manager.free_in_buffer != MaxBufferExtent)
1685 ERREXIT(cinfo,JERR_FILE_WRITE);
1686 destination->manager.next_output_byte=destination->buffer;
1687 return(TRUE);
1688}
1689
cristy1b58f252012-03-01 01:41:41 +00001690static QuantizationTable *GetQuantizationTable(const char *filename,
1691 const char *slot,ExceptionInfo *exception)
1692{
1693 char
1694 *p,
1695 *xml;
1696
1697 const char
1698 *attribute,
1699 *content;
1700
1701 double
1702 value;
1703
1704 register ssize_t
1705 i;
1706
1707 ssize_t
1708 j;
1709
1710 QuantizationTable
1711 *table;
1712
cristy1fcc8b62012-03-02 17:25:55 +00001713 size_t
1714 length;
1715
cristy1b58f252012-03-01 01:41:41 +00001716 XMLTreeInfo
1717 *description,
1718 *levels,
1719 *quantization_tables,
1720 *table_iterator;
1721
1722 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1723 "Loading quantization tables \"%s\" ...",filename);
1724 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001725 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001726 if (xml == (char *) NULL)
1727 return(table);
1728 quantization_tables=NewXMLTree(xml,exception);
1729 if (quantization_tables == (XMLTreeInfo *) NULL)
1730 {
1731 xml=DestroyString(xml);
1732 return(table);
1733 }
1734 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1735 table_iterator != (XMLTreeInfo *) NULL;
1736 table_iterator=GetNextXMLTreeTag(table_iterator))
1737 {
1738 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1739 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1740 break;
1741 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1742 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1743 break;
1744 }
1745 if (table_iterator == (XMLTreeInfo *) NULL)
1746 {
1747 xml=DestroyString(xml);
1748 return(table);
1749 }
1750 description=GetXMLTreeChild(table_iterator,"description");
1751 if (description == (XMLTreeInfo *) NULL)
1752 {
1753 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1754 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1755 quantization_tables=DestroyXMLTree(quantization_tables);
1756 xml=DestroyString(xml);
1757 return(table);
1758 }
1759 levels=GetXMLTreeChild(table_iterator,"levels");
1760 if (levels == (XMLTreeInfo *) NULL)
1761 {
1762 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1763 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1764 quantization_tables=DestroyXMLTree(quantization_tables);
1765 xml=DestroyString(xml);
1766 return(table);
1767 }
1768 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1769 if (table == (QuantizationTable *) NULL)
1770 ThrowFatalException(ResourceLimitFatalError,
1771 "UnableToAcquireQuantizationTable");
1772 table->slot=(char *) NULL;
1773 table->description=(char *) NULL;
1774 table->levels=(unsigned int *) NULL;
1775 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1776 if (attribute != (char *) NULL)
1777 table->slot=ConstantString(attribute);
1778 content=GetXMLTreeContent(description);
1779 if (content != (char *) NULL)
1780 table->description=ConstantString(content);
1781 attribute=GetXMLTreeAttribute(levels,"width");
1782 if (attribute == (char *) NULL)
1783 {
1784 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1785 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1786 quantization_tables=DestroyXMLTree(quantization_tables);
1787 table=DestroyQuantizationTable(table);
1788 xml=DestroyString(xml);
1789 return(table);
1790 }
1791 table->width=StringToUnsignedLong(attribute);
1792 if (table->width == 0)
1793 {
1794 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1795 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1796 quantization_tables=DestroyXMLTree(quantization_tables);
1797 table=DestroyQuantizationTable(table);
1798 xml=DestroyString(xml);
1799 return(table);
1800 }
1801 attribute=GetXMLTreeAttribute(levels,"height");
1802 if (attribute == (char *) NULL)
1803 {
1804 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1805 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1806 quantization_tables=DestroyXMLTree(quantization_tables);
1807 table=DestroyQuantizationTable(table);
1808 xml=DestroyString(xml);
1809 return(table);
1810 }
1811 table->height=StringToUnsignedLong(attribute);
1812 if (table->height == 0)
1813 {
1814 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1815 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1816 quantization_tables=DestroyXMLTree(quantization_tables);
1817 table=DestroyQuantizationTable(table);
1818 xml=DestroyString(xml);
1819 return(table);
1820 }
1821 attribute=GetXMLTreeAttribute(levels,"divisor");
1822 if (attribute == (char *) NULL)
1823 {
1824 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1825 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1826 quantization_tables=DestroyXMLTree(quantization_tables);
1827 table=DestroyQuantizationTable(table);
1828 xml=DestroyString(xml);
1829 return(table);
1830 }
cristy043f3f32012-03-02 17:37:28 +00001831 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1832 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001833 {
1834 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1835 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1836 quantization_tables=DestroyXMLTree(quantization_tables);
1837 table=DestroyQuantizationTable(table);
1838 xml=DestroyString(xml);
1839 return(table);
1840 }
1841 content=GetXMLTreeContent(levels);
1842 if (content == (char *) NULL)
1843 {
1844 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1845 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1846 quantization_tables=DestroyXMLTree(quantization_tables);
1847 table=DestroyQuantizationTable(table);
1848 xml=DestroyString(xml);
1849 return(table);
1850 }
cristy1fcc8b62012-03-02 17:25:55 +00001851 length=(size_t) table->width*table->height;
1852 if (length < 64)
1853 length=64;
cristy092006b2012-03-02 17:26:02 +00001854 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001855 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001856 if (table->levels == (unsigned int *) NULL)
1857 ThrowFatalException(ResourceLimitFatalError,
1858 "UnableToAcquireQuantizationTable");
1859 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1860 {
cristy043f3f32012-03-02 17:37:28 +00001861 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1862 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001863 while (isspace((int) ((unsigned char) *p)) != 0)
1864 p++;
1865 if (*p == ',')
1866 p++;
1867 content=p;
1868 }
cristy043f3f32012-03-02 17:37:28 +00001869 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001870 (void) value;
1871 if (p != content)
1872 {
1873 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1874 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1875 quantization_tables=DestroyXMLTree(quantization_tables);
1876 table=DestroyQuantizationTable(table);
1877 xml=DestroyString(xml);
1878 return(table);
1879 }
cristy043f3f32012-03-02 17:37:28 +00001880 for (j=i; j < 64; j++)
1881 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001882 quantization_tables=DestroyXMLTree(quantization_tables);
1883 xml=DestroyString(xml);
1884 return(table);
1885}
1886
cristy3ed852e2009-09-05 21:47:34 +00001887static void InitializeDestination(j_compress_ptr cinfo)
1888{
1889 DestinationManager
1890 *destination;
1891
1892 destination=(DestinationManager *) cinfo->dest;
1893 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1894 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1895 destination->manager.next_output_byte=destination->buffer;
1896 destination->manager.free_in_buffer=MaxBufferExtent;
1897}
1898
cristy3ed852e2009-09-05 21:47:34 +00001899static void TerminateDestination(j_compress_ptr cinfo)
1900{
1901 DestinationManager
1902 *destination;
1903
1904 destination=(DestinationManager *) cinfo->dest;
1905 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1906 {
1907 ssize_t
1908 count;
1909
1910 count=WriteBlob(destination->image,MaxBufferExtent-
1911 destination->manager.free_in_buffer,destination->buffer);
1912 if (count != (ssize_t)
1913 (MaxBufferExtent-destination->manager.free_in_buffer))
1914 ERREXIT(cinfo,JERR_FILE_WRITE);
1915 }
1916}
1917
1918static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1919{
1920 const char
1921 *name;
1922
1923 const StringInfo
1924 *profile;
1925
1926 MagickBooleanType
1927 iptc;
1928
cristybb503372010-05-27 20:51:26 +00001929 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001930 i;
1931
1932 size_t
cristy524222d2011-04-25 00:37:06 +00001933 length,
1934 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001935
1936 StringInfo
1937 *custom_profile;
1938
cristy3ed852e2009-09-05 21:47:34 +00001939 /*
1940 Save image profile as a APP marker.
1941 */
1942 iptc=MagickFalse;
1943 custom_profile=AcquireStringInfo(65535L);
1944 ResetImageProfileIterator(image);
1945 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1946 {
cristy109e5572010-09-16 18:38:17 +00001947 register unsigned char
1948 *p;
1949
cristy3ed852e2009-09-05 21:47:34 +00001950 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001951 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001952 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001953 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001954 {
1955 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1956 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1957 (unsigned int) length);
1958 }
1959 if (LocaleCompare(name,"ICC") == 0)
1960 {
1961 register unsigned char
1962 *p;
1963
cristy8ba82ae2013-05-29 14:19:49 +00001964 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001965 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001966 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001967 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001968 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001969 {
1970 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001971 p[12]=(unsigned char) ((i/65519L)+1);
1972 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001973 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001974 length);
1975 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001976 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001977 }
1978 }
1979 if (((LocaleCompare(name,"IPTC") == 0) ||
1980 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1981 {
cristybb503372010-05-27 20:51:26 +00001982 size_t
cristy3ed852e2009-09-05 21:47:34 +00001983 roundup;
1984
1985 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001986 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001987 {
1988 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001989 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001990 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1991 {
1992 (void) memcpy(p,"Photoshop 3.0 ",14);
1993 tag_length=14;
1994 }
1995 else
1996 {
1997 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1998 tag_length=26;
1999 p[24]=(unsigned char) (length >> 8);
2000 p[25]=(unsigned char) (length & 0xff);
2001 }
2002 p[13]=0x00;
2003 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00002004 if (roundup != 0)
2005 p[length+tag_length]='\0';
2006 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
2007 custom_profile),(unsigned int) (length+tag_length+roundup));
2008 }
2009 }
2010 if (LocaleCompare(name,"XMP") == 0)
2011 {
2012 StringInfo
2013 *xmp_profile;
2014
2015 /*
2016 Add namespace to XMP profile.
2017 */
cristy0615f0e2011-10-12 11:36:46 +00002018 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy30841e62014-05-19 00:45:15 +00002019 if (xmp_profile != (StringInfo *) NULL)
cristy8418c7e2014-05-18 18:38:26 +00002020 {
cristyc08a0aa2014-05-25 23:30:09 +00002021 if (profile != (StringInfo *) NULL)
2022 ConcatenateStringInfo(xmp_profile,profile);
cristy8418c7e2014-05-18 18:38:26 +00002023 GetStringInfoDatum(xmp_profile)[28]='\0';
2024 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
2025 {
2026 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
2027 jpeg_write_marker(jpeg_info,XML_MARKER,
2028 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
2029 }
2030 xmp_profile=DestroyStringInfo(xmp_profile);
2031 }
cristy3ed852e2009-09-05 21:47:34 +00002032 }
cristye8c25f92010-06-03 00:53:06 +00002033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2034 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00002035 name=GetNextImageProfile(image);
2036 }
2037 custom_profile=DestroyStringInfo(custom_profile);
2038}
2039
2040static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
2041{
2042 DestinationManager
2043 *destination;
2044
2045 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
2046 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
2047 destination=(DestinationManager *) cinfo->dest;
2048 destination->manager.init_destination=InitializeDestination;
2049 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2050 destination->manager.term_destination=TerminateDestination;
2051 destination->image=image;
2052}
2053
2054static char **SamplingFactorToList(const char *text)
2055{
2056 char
2057 **textlist;
2058
2059 register char
2060 *q;
2061
2062 register const char
2063 *p;
2064
cristybb503372010-05-27 20:51:26 +00002065 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002066 i;
2067
cristybb503372010-05-27 20:51:26 +00002068 size_t
cristy3ed852e2009-09-05 21:47:34 +00002069 lines;
2070
2071 if (text == (char *) NULL)
2072 return((char **) NULL);
2073 /*
2074 Convert string to an ASCII list.
2075 */
2076 lines=1;
2077 for (p=text; *p != '\0'; p++)
2078 if (*p == ',')
2079 lines++;
2080 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
2081 sizeof(*textlist));
2082 if (textlist == (char **) NULL)
2083 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2084 p=text;
cristybb503372010-05-27 20:51:26 +00002085 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00002086 {
2087 for (q=(char *) p; *q != '\0'; q++)
2088 if (*q == ',')
2089 break;
2090 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
2091 sizeof(*textlist[i]));
2092 if (textlist[i] == (char *) NULL)
2093 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2094 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2095 if (*q == '\r')
2096 q++;
2097 p=q+1;
2098 }
2099 textlist[i]=(char *) NULL;
2100 return(textlist);
2101}
2102
2103static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002104 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002105{
2106 const char
2107 *option,
2108 *sampling_factor,
2109 *value;
2110
2111 ErrorManager
2112 error_manager;
2113
cristy21749e92012-02-18 02:29:21 +00002114 int
cristy7b8ed292013-05-18 01:13:56 +00002115 colorspace,
cristy21749e92012-02-18 02:29:21 +00002116 quality;
2117
cristy3ed852e2009-09-05 21:47:34 +00002118 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002119 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002120
2121 JSAMPROW
2122 scanline[1];
2123
cristy3ed852e2009-09-05 21:47:34 +00002124 MagickBooleanType
2125 status;
2126
cristy22646e22013-06-23 16:34:03 +00002127 MemoryInfo
2128 *memory_info;
2129
cristy3ed852e2009-09-05 21:47:34 +00002130 register JSAMPLE
2131 *q;
2132
cristybb503372010-05-27 20:51:26 +00002133 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002134 i;
2135
cristy524222d2011-04-25 00:37:06 +00002136 ssize_t
2137 y;
2138
cristy3ed852e2009-09-05 21:47:34 +00002139 struct jpeg_compress_struct
2140 jpeg_info;
2141
2142 struct jpeg_error_mgr
2143 jpeg_error;
2144
cristy5328a4c2013-12-03 11:32:13 +00002145 unsigned short
2146 scale;
2147
cristy3ed852e2009-09-05 21:47:34 +00002148 /*
2149 Open image file.
2150 */
2151 assert(image_info != (const ImageInfo *) NULL);
2152 assert(image_info->signature == MagickSignature);
2153 assert(image != (Image *) NULL);
2154 assert(image->signature == MagickSignature);
2155 if (image->debug != MagickFalse)
2156 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002157 assert(exception != (ExceptionInfo *) NULL);
2158 assert(exception->signature == MagickSignature);
cristya663a4f2015-01-23 12:11:57 +00002159 if ((LocaleCompare(image_info->magick,"JPS") == 0) &&
2160 (image->next != (Image *) NULL))
2161 image=AppendImages(image,MagickFalse,exception);
cristy73ef4eb2015-01-23 14:31:57 +00002162 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2163 if (status == MagickFalse)
2164 return(status);
cristy3ed852e2009-09-05 21:47:34 +00002165 /*
2166 Initialize JPEG parameters.
2167 */
cristy91044972011-04-22 14:21:16 +00002168 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002169 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2170 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2171 jpeg_info.client_data=(void *) image;
2172 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002173 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002174 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002175 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002176 error_manager.image=image;
cristy72f87f82013-06-23 16:43:05 +00002177 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002178 if (setjmp(error_manager.error_recovery) != 0)
2179 {
2180 jpeg_destroy_compress(&jpeg_info);
2181 (void) CloseBlob(image);
2182 return(MagickFalse);
2183 }
2184 jpeg_info.client_data=(void *) &error_manager;
2185 jpeg_create_compress(&jpeg_info);
2186 JPEGDestinationManager(&jpeg_info,image);
2187 if ((image->columns != (unsigned int) image->columns) ||
2188 (image->rows != (unsigned int) image->rows))
2189 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2190 jpeg_info.image_width=(unsigned int) image->columns;
2191 jpeg_info.image_height=(unsigned int) image->rows;
2192 jpeg_info.input_components=3;
2193 jpeg_info.data_precision=8;
2194 jpeg_info.in_color_space=JCS_RGB;
2195 switch (image->colorspace)
2196 {
2197 case CMYKColorspace:
2198 {
2199 jpeg_info.input_components=4;
2200 jpeg_info.in_color_space=JCS_CMYK;
2201 break;
2202 }
2203 case YCbCrColorspace:
2204 case Rec601YCbCrColorspace:
2205 case Rec709YCbCrColorspace:
2206 {
2207 jpeg_info.in_color_space=JCS_YCbCr;
2208 break;
2209 }
2210 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002211 {
cristye9355552014-01-16 13:35:05 +00002212 if (image_info->type == TrueColorType)
2213 break;
cristy3ed852e2009-09-05 21:47:34 +00002214 jpeg_info.input_components=1;
2215 jpeg_info.in_color_space=JCS_GRAYSCALE;
2216 break;
2217 }
2218 default:
cristy9f396782009-12-21 01:37:45 +00002219 {
cristyaf8d3912014-02-21 14:50:33 +00002220 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye9355552014-01-16 13:35:05 +00002221 if (image_info->type == TrueColorType)
2222 break;
dirkf1d85482015-04-06 00:36:00 +00002223 if (SetImageGray(image,exception) != MagickFalse)
cristye9355552014-01-16 13:35:05 +00002224 {
2225 jpeg_info.input_components=1;
2226 jpeg_info.in_color_space=JCS_GRAYSCALE;
2227 }
cristy3ed852e2009-09-05 21:47:34 +00002228 break;
cristy9f396782009-12-21 01:37:45 +00002229 }
cristy3ed852e2009-09-05 21:47:34 +00002230 }
cristy3ed852e2009-09-05 21:47:34 +00002231 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002232 if (jpeg_info.in_color_space == JCS_CMYK)
2233 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002234 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2235 jpeg_info.data_precision=8;
2236 else
dirk93b02b72013-11-16 16:03:36 +00002237 jpeg_info.data_precision=BITS_IN_JSAMPLE;
cristy4d30a482014-10-19 12:58:39 +00002238 jpeg_info.density_unit=1;
cristy3ed852e2009-09-05 21:47:34 +00002239 if (image->debug != MagickFalse)
2240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002241 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2242 floor(image->resolution.y+0.5));
2243 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002244 {
2245 /*
2246 Set image resolution.
2247 */
cristydf878012014-01-12 23:39:40 +00002248 jpeg_info.write_JFIF_header=TRUE;
cristy2a11bef2011-10-28 18:33:11 +00002249 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2250 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy0c220632013-09-25 22:10:28 +00002251 /*
2252 Set image resolution units.
2253 */
2254 jpeg_info.density_unit=(UINT8) 0;
cristy3ed852e2009-09-05 21:47:34 +00002255 if (image->units == PixelsPerInchResolution)
2256 jpeg_info.density_unit=(UINT8) 1;
2257 if (image->units == PixelsPerCentimeterResolution)
2258 jpeg_info.density_unit=(UINT8) 2;
2259 }
cristy97cb3bf2012-02-21 18:31:20 +00002260 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002261 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002262 if (option != (const char *) NULL)
2263 switch (*option)
2264 {
2265 case 'D':
2266 case 'd':
2267 {
2268 if (LocaleCompare(option,"default") == 0)
2269 jpeg_info.dct_method=JDCT_DEFAULT;
2270 break;
2271 }
2272 case 'F':
2273 case 'f':
2274 {
2275 if (LocaleCompare(option,"fastest") == 0)
2276 jpeg_info.dct_method=JDCT_FASTEST;
2277 if (LocaleCompare(option,"float") == 0)
2278 jpeg_info.dct_method=JDCT_FLOAT;
2279 break;
2280 }
2281 case 'I':
2282 case 'i':
2283 {
2284 if (LocaleCompare(option,"ifast") == 0)
2285 jpeg_info.dct_method=JDCT_IFAST;
2286 if (LocaleCompare(option,"islow") == 0)
2287 jpeg_info.dct_method=JDCT_ISLOW;
2288 break;
2289 }
2290 }
cristy092ec8d2013-04-26 13:46:22 +00002291 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002292 if (option != (const char *) NULL)
cristyb9d0e7e2014-11-02 18:14:44 +00002293 jpeg_info.optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE :
2294 FALSE;
cristy7d31f6e2014-11-11 13:07:58 +00002295 else
2296 {
2297 MagickSizeType
2298 length;
2299
2300 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2301 image->rows*sizeof(JSAMPLE);
2302 if (length == (MagickSizeType) ((size_t) length))
2303 {
2304 /*
2305 Perform optimization only if available memory resources permit it.
2306 */
2307 status=AcquireMagickResource(MemoryResource,length);
2308 RelinquishMagickResource(MemoryResource,length);
2309 jpeg_info.optimize_coding=status == MagickFalse ? FALSE : TRUE;
2310 }
2311 }
cristy3ed852e2009-09-05 21:47:34 +00002312#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2313 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2314 (image_info->interlace != NoInterlace))
2315 {
2316 if (image->debug != MagickFalse)
2317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2318 "Interlace: progressive");
2319 jpeg_simple_progression(&jpeg_info);
2320 }
2321 else
2322 if (image->debug != MagickFalse)
2323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2324 "Interlace: non-progressive");
2325#else
2326 if (image->debug != MagickFalse)
2327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2328 "Interlace: nonprogressive");
2329#endif
dirk871abab2013-11-03 13:21:16 +00002330 quality=92;
cristy2a950402014-03-12 17:57:14 +00002331 if ((image_info->compression != LosslessJPEGCompression) &&
2332 (image->quality <= 100))
2333 {
2334 if (image->quality != UndefinedCompressionQuality)
2335 quality=(int) image->quality;
2336 if (image->debug != MagickFalse)
2337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2338 (double) image->quality);
2339 }
2340 else
2341 {
2342#if !defined(C_LOSSLESS_SUPPORTED)
2343 quality=100;
2344 if (image->debug != MagickFalse)
2345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2346#else
2347 if (image->quality < 100)
2348 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2349 "LosslessToLossyJPEGConversion",image->filename);
2350 else
2351 {
2352 int
2353 point_transform,
2354 predictor;
2355
2356 predictor=image->quality/100; /* range 1-7 */
2357 point_transform=image->quality % 20; /* range 0-15 */
2358 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2359 if (image->debug != MagickFalse)
2360 {
2361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2362 "Compression: lossless");
2363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2364 "Predictor: %d",predictor);
2365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2366 "Point Transform: %d",point_transform);
2367 }
2368 }
2369#endif
2370 }
cristy092ec8d2013-04-26 13:46:22 +00002371 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002372 if (option != (const char *) NULL)
2373 {
2374 Image
2375 *jpeg_image;
2376
2377 ImageInfo
2378 *jpeg_info;
2379
2380 jpeg_info=CloneImageInfo(image_info);
cristye0823d52013-11-03 21:50:10 +00002381 jpeg_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002382 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002383 if (jpeg_image != (Image *) NULL)
2384 {
2385 MagickSizeType
2386 extent;
2387
2388 size_t
cristy87e73ab2010-02-12 01:59:12 +00002389 maximum,
2390 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002391
2392 /*
2393 Search for compression quality that does not exceed image extent.
2394 */
2395 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002396 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002397 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002398 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
cristy4e8b2ed2015-03-06 19:54:18 +00002399 maximum=image->quality;
2400 if (maximum < 2)
2401 maximum=2;
dirkcf845242013-08-11 21:36:19 +00002402 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002403 {
cristy3975f402014-02-26 14:36:33 +00002404 (void) AcquireUniqueFilename(jpeg_image->filename);
2405 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
cristy1e178e72011-08-28 19:44:34 +00002406 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002407 if (GetBlobSize(jpeg_image) <= extent)
cristy3975f402014-02-26 14:36:33 +00002408 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002409 else
cristy3975f402014-02-26 14:36:33 +00002410 maximum=jpeg_image->quality-1;
2411 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristy0adb4f92009-11-28 18:08:51 +00002412 }
cristy58d4fe12015-02-07 11:53:05 +00002413 quality=(int) minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002414 jpeg_image=DestroyImage(jpeg_image);
2415 }
2416 jpeg_info=DestroyImageInfo(jpeg_info);
2417 }
cristy9bb7c842014-06-17 23:42:24 +00002418 jpeg_set_quality(&jpeg_info,quality,TRUE);
cristy16e74722012-07-01 13:01:38 +00002419#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002420 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002421 if (option != (const char *) NULL)
2422 {
2423 GeometryInfo
2424 geometry_info;
2425
2426 int
2427 flags;
2428
2429 /*
2430 Set quality scaling for luminance and chrominance separately.
2431 */
2432 flags=ParseGeometry(option,&geometry_info);
2433 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2434 {
2435 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2436 (geometry_info.rho+0.5));
2437 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2438 (geometry_info.sigma+0.5));
cristy9bb7c842014-06-17 23:42:24 +00002439 jpeg_default_qtables(&jpeg_info,TRUE);
cristy16e74722012-07-01 13:01:38 +00002440 }
2441 }
2442#endif
cristy7b8ed292013-05-18 01:13:56 +00002443 colorspace=jpeg_info.in_color_space;
2444 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002445 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002446 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002447 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002448 colorspace=StringToInteger(value);
2449 sampling_factor=(const char *) NULL;
2450 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002451 {
cristy7b8ed292013-05-18 01:13:56 +00002452 value=GetImageOption(image_info,"jpeg:sampling-factor");
2453 if (value == (char *) NULL)
2454 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2455 if (value != (char *) NULL)
2456 {
2457 sampling_factor=value;
2458 if (image->debug != MagickFalse)
2459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2460 " Input sampling-factors=%s",sampling_factor);
2461 }
cristy3ed852e2009-09-05 21:47:34 +00002462 }
cristy7b8ed292013-05-18 01:13:56 +00002463 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002464 if (image_info->sampling_factor != (char *) NULL)
2465 sampling_factor=image_info->sampling_factor;
2466 if (sampling_factor == (const char *) NULL)
2467 {
cristyc243f622015-02-21 17:20:34 +00002468 if (quality >= 90)
cristy3ed852e2009-09-05 21:47:34 +00002469 for (i=0; i < MAX_COMPONENTS; i++)
2470 {
2471 jpeg_info.comp_info[i].h_samp_factor=1;
2472 jpeg_info.comp_info[i].v_samp_factor=1;
2473 }
2474 }
2475 else
2476 {
2477 char
2478 **factors;
2479
2480 GeometryInfo
2481 geometry_info;
2482
2483 MagickStatusType
2484 flags;
2485
2486 /*
2487 Set sampling factor.
2488 */
2489 i=0;
2490 factors=SamplingFactorToList(sampling_factor);
2491 if (factors != (char **) NULL)
2492 {
2493 for (i=0; i < MAX_COMPONENTS; i++)
2494 {
2495 if (factors[i] == (char *) NULL)
2496 break;
2497 flags=ParseGeometry(factors[i],&geometry_info);
2498 if ((flags & SigmaValue) == 0)
2499 geometry_info.sigma=geometry_info.rho;
2500 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2501 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2502 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2503 }
2504 factors=(char **) RelinquishMagickMemory(factors);
2505 }
2506 for ( ; i < MAX_COMPONENTS; i++)
2507 {
2508 jpeg_info.comp_info[i].h_samp_factor=1;
2509 jpeg_info.comp_info[i].v_samp_factor=1;
2510 }
2511 }
cristy092ec8d2013-04-26 13:46:22 +00002512 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002513 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002514 {
cristy1b58f252012-03-01 01:41:41 +00002515 QuantizationTable
2516 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002517
cristy1445b322012-02-19 18:57:30 +00002518 /*
cristy1b58f252012-03-01 01:41:41 +00002519 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002520 */
cristy1b58f252012-03-01 01:41:41 +00002521 table=GetQuantizationTable(option,"0",exception);
2522 if (table != (QuantizationTable *) NULL)
2523 {
cristyae453b72013-04-21 22:24:20 +00002524 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002525 jpeg_info.comp_info[i].quant_tbl_no=0;
2526 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2527 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002528 table=DestroyQuantizationTable(table);
2529 }
2530 table=GetQuantizationTable(option,"1",exception);
2531 if (table != (QuantizationTable *) NULL)
2532 {
cristyae453b72013-04-21 22:24:20 +00002533 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002534 jpeg_info.comp_info[i].quant_tbl_no=1;
2535 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2536 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002537 table=DestroyQuantizationTable(table);
2538 }
2539 table=GetQuantizationTable(option,"2",exception);
2540 if (table != (QuantizationTable *) NULL)
2541 {
cristyae453b72013-04-21 22:24:20 +00002542 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002543 jpeg_info.comp_info[i].quant_tbl_no=2;
2544 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2545 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002546 table=DestroyQuantizationTable(table);
2547 }
2548 table=GetQuantizationTable(option,"3",exception);
2549 if (table != (QuantizationTable *) NULL)
2550 {
cristyae453b72013-04-21 22:24:20 +00002551 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002552 jpeg_info.comp_info[i].quant_tbl_no=3;
2553 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2554 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002555 table=DestroyQuantizationTable(table);
2556 }
cristy1445b322012-02-19 18:57:30 +00002557 }
cristy9bb7c842014-06-17 23:42:24 +00002558 jpeg_start_compress(&jpeg_info,TRUE);
cristy3ed852e2009-09-05 21:47:34 +00002559 if (image->debug != MagickFalse)
2560 {
2561 if (image->storage_class == PseudoClass)
2562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2563 "Storage class: PseudoClass");
2564 else
2565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2566 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2568 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002569 if (image->colors != 0)
2570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002571 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002572 else
2573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2574 "Number of colors: unspecified");
2575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2576 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2577 switch (image->colorspace)
2578 {
2579 case CMYKColorspace:
2580 {
2581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 "Storage class: DirectClass");
2583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2584 "Colorspace: CMYK");
2585 break;
2586 }
2587 case YCbCrColorspace:
2588 case Rec601YCbCrColorspace:
2589 case Rec709YCbCrColorspace:
2590 {
2591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2592 "Colorspace: YCbCr");
2593 break;
2594 }
2595 default:
2596 break;
2597 }
2598 switch (image->colorspace)
2599 {
2600 case CMYKColorspace:
2601 {
2602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2603 "Colorspace: CMYK");
2604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2605 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2606 jpeg_info.comp_info[0].h_samp_factor,
2607 jpeg_info.comp_info[0].v_samp_factor,
2608 jpeg_info.comp_info[1].h_samp_factor,
2609 jpeg_info.comp_info[1].v_samp_factor,
2610 jpeg_info.comp_info[2].h_samp_factor,
2611 jpeg_info.comp_info[2].v_samp_factor,
2612 jpeg_info.comp_info[3].h_samp_factor,
2613 jpeg_info.comp_info[3].v_samp_factor);
2614 break;
2615 }
2616 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002617 {
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 "Colorspace: GRAY");
2620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2621 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2622 jpeg_info.comp_info[0].v_samp_factor);
2623 break;
2624 }
cristyed8d7852013-08-01 22:44:22 +00002625 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002626 case RGBColorspace:
2627 {
2628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002629 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2631 "Sampling factors: %dx%d,%dx%d,%dx%d",
2632 jpeg_info.comp_info[0].h_samp_factor,
2633 jpeg_info.comp_info[0].v_samp_factor,
2634 jpeg_info.comp_info[1].h_samp_factor,
2635 jpeg_info.comp_info[1].v_samp_factor,
2636 jpeg_info.comp_info[2].h_samp_factor,
2637 jpeg_info.comp_info[2].v_samp_factor);
2638 break;
2639 }
2640 case YCbCrColorspace:
2641 case Rec601YCbCrColorspace:
2642 case Rec709YCbCrColorspace:
2643 {
2644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2645 "Colorspace: YCbCr");
2646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2647 "Sampling factors: %dx%d,%dx%d,%dx%d",
2648 jpeg_info.comp_info[0].h_samp_factor,
2649 jpeg_info.comp_info[0].v_samp_factor,
2650 jpeg_info.comp_info[1].h_samp_factor,
2651 jpeg_info.comp_info[1].v_samp_factor,
2652 jpeg_info.comp_info[2].h_samp_factor,
2653 jpeg_info.comp_info[2].v_samp_factor);
2654 break;
2655 }
2656 default:
2657 {
2658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2659 image->colorspace);
2660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2661 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2662 jpeg_info.comp_info[0].h_samp_factor,
2663 jpeg_info.comp_info[0].v_samp_factor,
2664 jpeg_info.comp_info[1].h_samp_factor,
2665 jpeg_info.comp_info[1].v_samp_factor,
2666 jpeg_info.comp_info[2].h_samp_factor,
2667 jpeg_info.comp_info[2].v_samp_factor,
2668 jpeg_info.comp_info[3].h_samp_factor,
2669 jpeg_info.comp_info[3].v_samp_factor);
2670 break;
2671 }
2672 }
2673 }
2674 /*
2675 Write JPEG profiles.
2676 */
cristyd15e6592011-10-15 00:13:06 +00002677 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002678 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002679 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002680 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2681 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2682 if (image->profiles != (void *) NULL)
2683 WriteProfile(&jpeg_info,image);
2684 /*
2685 Convert MIFF to JPEG raster pixels.
2686 */
cristy22646e22013-06-23 16:34:03 +00002687 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002688 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002689 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002690 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002691 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002692 if (setjmp(error_manager.error_recovery) != 0)
2693 {
2694 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002695 if (memory_info != (MemoryInfo *) NULL)
2696 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002697 (void) CloseBlob(image);
2698 return(MagickFalse);
2699 }
2700 scanline[0]=(JSAMPROW) jpeg_pixels;
cristy477b9fd2013-12-03 16:52:31 +00002701 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy31db6c12013-12-04 15:17:30 +00002702 if (scale == 0)
2703 scale=1;
cristye90d7402010-03-14 18:21:29 +00002704 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002705 {
cristy3ed852e2009-09-05 21:47:34 +00002706 if ((jpeg_info.in_color_space == JCS_RGB) ||
2707 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002708 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002709 {
cristy4c08aed2011-07-01 19:47:50 +00002710 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002711 *p;
2712
cristybb503372010-05-27 20:51:26 +00002713 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002714 x;
2715
cristy1e178e72011-08-28 19:44:34 +00002716 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002717 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002718 break;
2719 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002720 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002721 {
cristy4c08aed2011-07-01 19:47:50 +00002722 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2723 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2724 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002725 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002726 }
2727 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002728 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2729 image->rows);
2730 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002731 break;
2732 }
2733 else
cristye90d7402010-03-14 18:21:29 +00002734 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002735 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002736 {
cristyd0323222013-04-07 16:13:21 +00002737 register const Quantum
2738 *p;
2739
2740 register ssize_t
2741 x;
2742
2743 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2744 if (p == (const Quantum *) NULL)
2745 break;
2746 q=jpeg_pixels;
2747 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002748 {
cristyd0323222013-04-07 16:13:21 +00002749 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2750 image,p)));
2751 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002752 }
cristyd0323222013-04-07 16:13:21 +00002753 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2754 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2755 image->rows);
2756 if (status == MagickFalse)
2757 break;
2758 }
cristye90d7402010-03-14 18:21:29 +00002759 else
cristybb503372010-05-27 20:51:26 +00002760 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002761 {
cristy4c08aed2011-07-01 19:47:50 +00002762 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002763 *p;
2764
cristybb503372010-05-27 20:51:26 +00002765 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002766 x;
2767
cristy1e178e72011-08-28 19:44:34 +00002768 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002769 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002770 break;
2771 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002772 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002773 {
2774 /*
2775 Convert DirectClass packets to contiguous CMYK scanlines.
2776 */
2777 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002778 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002779 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002780 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002781 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002782 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002783 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002784 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002785 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002786 }
2787 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002788 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2789 image->rows);
2790 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002791 break;
2792 }
2793 }
2794 else
2795 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002796 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002797 {
cristy4c08aed2011-07-01 19:47:50 +00002798 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002799 *p;
2800
cristybb503372010-05-27 20:51:26 +00002801 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002802 x;
2803
cristy1e178e72011-08-28 19:44:34 +00002804 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002805 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002806 break;
2807 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002808 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002809 {
cristy217ddd62013-12-06 23:27:09 +00002810 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2811 p)))/scale);
cristyed231572011-07-14 02:18:59 +00002812 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002813 }
2814 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002815 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2816 image->rows);
2817 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002818 break;
2819 }
2820 else
2821 if ((jpeg_info.in_color_space == JCS_RGB) ||
2822 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002823 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002824 {
cristy4c08aed2011-07-01 19:47:50 +00002825 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002826 *p;
2827
cristybb503372010-05-27 20:51:26 +00002828 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002829 x;
2830
cristy1e178e72011-08-28 19:44:34 +00002831 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002832 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002833 break;
2834 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002835 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002836 {
cristy5328a4c2013-12-03 11:32:13 +00002837 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2838 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2839 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002840 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002841 }
2842 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002843 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002844 image->rows);
cristye90d7402010-03-14 18:21:29 +00002845 if (status == MagickFalse)
2846 break;
2847 }
2848 else
cristybb503372010-05-27 20:51:26 +00002849 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002850 {
cristy4c08aed2011-07-01 19:47:50 +00002851 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002852 *p;
2853
cristybb503372010-05-27 20:51:26 +00002854 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002855 x;
2856
cristy1e178e72011-08-28 19:44:34 +00002857 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002858 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002859 break;
2860 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002861 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002862 {
2863 /*
2864 Convert DirectClass packets to contiguous CMYK scanlines.
2865 */
cristy217ddd62013-12-06 23:27:09 +00002866 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2867 image,p))/scale);
2868 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2869 image,p))/scale);
2870 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2871 image,p))/scale);
2872 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2873 image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002874 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002875 }
2876 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002877 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002878 image->rows);
cristye90d7402010-03-14 18:21:29 +00002879 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002880 break;
2881 }
cristybb503372010-05-27 20:51:26 +00002882 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002883 jpeg_finish_compress(&jpeg_info);
2884 /*
2885 Relinquish resources.
2886 */
2887 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002888 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002889 (void) CloseBlob(image);
2890 return(MagickTrue);
2891}
2892#endif