blob: b88768db052341025aeb1efb14282d594cd7fa6f [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)
1305 return(DestroyImageList(image));
cristy22646e22013-06-23 16:34:03 +00001306 memory_info=AcquireVirtualMemory((size_t) image->columns,
1307 jpeg_info.output_components*sizeof(*jpeg_pixels));
1308 if (memory_info == (MemoryInfo *) NULL)
cristyee1bdaa2013-07-16 16:45:03 +00001309 {
1310 jpeg_destroy_decompress(&jpeg_info);
1311 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1312 }
cristy22646e22013-06-23 16:34:03 +00001313 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001314 /*
1315 Convert JPEG pixels to pixel packets.
1316 */
1317 if (setjmp(error_manager.error_recovery) != 0)
1318 {
cristy22646e22013-06-23 16:34:03 +00001319 if (memory_info != (MemoryInfo *) NULL)
1320 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001321 jpeg_destroy_decompress(&jpeg_info);
1322 (void) CloseBlob(image);
1323 number_pixels=(MagickSizeType) image->columns*image->rows;
1324 if (number_pixels != 0)
1325 return(GetFirstImageInList(image));
1326 return(DestroyImage(image));
1327 }
1328 if (jpeg_info.quantize_colors != MagickFalse)
1329 {
cristybb503372010-05-27 20:51:26 +00001330 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001331 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001332 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001333 {
cristy1b58f252012-03-01 01:41:41 +00001334 image->colormap[i].red=(double) ScaleCharToQuantum(
1335 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001336 image->colormap[i].green=image->colormap[i].red;
1337 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001338 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001339 }
1340 else
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]);
1345 image->colormap[i].green=(double) ScaleCharToQuantum(
1346 jpeg_info.colormap[1][i]);
1347 image->colormap[i].blue=(double) ScaleCharToQuantum(
1348 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001349 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001350 }
1351 }
1352 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001353 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001354 {
cristybb503372010-05-27 20:51:26 +00001355 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001356 x;
1357
cristy4c08aed2011-07-01 19:47:50 +00001358 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001359 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001360
1361 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1362 {
1363 (void) ThrowMagickException(exception,GetMagickModule(),
1364 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1365 continue;
1366 }
1367 p=jpeg_pixels;
1368 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001369 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001370 break;
cristy3ed852e2009-09-05 21:47:34 +00001371 if (jpeg_info.data_precision > 8)
1372 {
cristy5328a4c2013-12-03 11:32:13 +00001373 unsigned short
1374 scale;
1375
cristy477b9fd2013-12-03 16:52:31 +00001376 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy3ed852e2009-09-05 21:47:34 +00001377 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001378 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001379 {
cristybb503372010-05-27 20:51:26 +00001380 size_t
cristy3ed852e2009-09-05 21:47:34 +00001381 pixel;
1382
cristy5328a4c2013-12-03 11:32:13 +00001383 pixel=(size_t) (scale*GETJSAMPLE(*p));
cristyc82a27b2011-10-21 01:07:16 +00001384 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001385 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001386 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001387 p++;
cristyed231572011-07-14 02:18:59 +00001388 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001389 }
1390 else
1391 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001392 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001393 {
cristy217ddd62013-12-06 23:27:09 +00001394 SetPixelRed(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1395 q);
1396 SetPixelGreen(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1397 q);
1398 SetPixelBlue(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1399 q);
cristy4c08aed2011-07-01 19:47:50 +00001400 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001401 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001402 }
1403 else
cristybb503372010-05-27 20:51:26 +00001404 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001405 {
cristy217ddd62013-12-06 23:27:09 +00001406 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(scale*
1407 GETJSAMPLE(*p++)),q);
1408 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(scale*
1409 GETJSAMPLE(*p++)),q);
1410 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(scale*
1411 GETJSAMPLE(*p++)),q);
1412 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(scale*
1413 GETJSAMPLE(*p++)),q);
cristy4c08aed2011-07-01 19:47:50 +00001414 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001415 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001416 }
1417 }
1418 else
1419 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001420 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001421 {
cristyc82a27b2011-10-21 01:07:16 +00001422 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001423 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001424 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001425 p++;
cristyed231572011-07-14 02:18:59 +00001426 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001427 }
1428 else
1429 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001430 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001431 {
cristy4c08aed2011-07-01 19:47:50 +00001432 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1433 GETJSAMPLE(*p++)),q);
1434 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1435 GETJSAMPLE(*p++)),q);
1436 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1437 GETJSAMPLE(*p++)),q);
1438 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001439 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001440 }
1441 else
cristybb503372010-05-27 20:51:26 +00001442 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001443 {
cristy4c08aed2011-07-01 19:47:50 +00001444 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1445 (unsigned char) GETJSAMPLE(*p++)),q);
1446 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1447 (unsigned char) GETJSAMPLE(*p++)),q);
1448 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1449 (unsigned char) GETJSAMPLE(*p++)),q);
1450 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1451 (unsigned char) GETJSAMPLE(*p++)),q);
1452 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001453 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001454 }
1455 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1456 break;
cristy524222d2011-04-25 00:37:06 +00001457 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1458 image->rows);
1459 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001460 {
1461 jpeg_abort_decompress(&jpeg_info);
1462 break;
1463 }
cristy3ed852e2009-09-05 21:47:34 +00001464 }
cristyd28b1dd2011-05-14 20:30:38 +00001465 if (status != MagickFalse)
1466 {
1467 error_manager.finished=MagickTrue;
1468 if (setjmp(error_manager.error_recovery) == 0)
1469 (void) jpeg_finish_decompress(&jpeg_info);
1470 }
cristy3ed852e2009-09-05 21:47:34 +00001471 /*
1472 Free jpeg resources.
1473 */
cristy3ed852e2009-09-05 21:47:34 +00001474 jpeg_destroy_decompress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00001475 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001476 (void) CloseBlob(image);
1477 return(GetFirstImageInList(image));
1478}
1479#endif
dirk29dd80e2013-10-31 23:11:11 +00001480
1481
cristy3ed852e2009-09-05 21:47:34 +00001482/*
1483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484% %
1485% %
1486% %
1487% R e g i s t e r J P E G I m a g e %
1488% %
1489% %
1490% %
1491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492%
1493% RegisterJPEGImage() adds properties for the JPEG image format to
1494% the list of supported formats. The properties include the image format
1495% tag, a method to read and/or write the format, whether the format
1496% supports the saving of more than one frame to the same file or blob,
1497% whether the format supports native in-memory I/O, and a brief
1498% description of the format.
1499%
1500% The format of the RegisterJPEGImage method is:
1501%
cristybb503372010-05-27 20:51:26 +00001502% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001503%
1504*/
cristybb503372010-05-27 20:51:26 +00001505ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001506{
1507 char
1508 version[MaxTextExtent];
1509
1510 MagickInfo
1511 *entry;
1512
1513 static const char
cristy7138c592009-09-08 13:58:52 +00001514 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001515
1516 *version='\0';
1517#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001518 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001519#endif
cristyb338b712014-11-27 14:06:34 +00001520 entry=SetMagickInfo("JPE");
cristy8e62a802014-12-27 22:43:35 +00001521#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristyb338b712014-11-27 14:06:34 +00001522 entry->thread_support=NoThreadSupport;
1523#endif
1524#if defined(MAGICKCORE_JPEG_DELEGATE)
1525 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1526 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1527#endif
1528 entry->magick=(IsImageFormatHandler *) IsJPEG;
1529 entry->adjoin=MagickFalse;
1530 entry->description=ConstantString(description);
1531 if (*version != '\0')
1532 entry->version=ConstantString(version);
1533 entry->mime_type=ConstantString("image/jpeg");
1534 entry->module=ConstantString("JPEG");
1535 (void) RegisterMagickInfo(entry);
cristy3ed852e2009-09-05 21:47:34 +00001536 entry=SetMagickInfo("JPEG");
cristy8e62a802014-12-27 22:43:35 +00001537#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001538 entry->thread_support=NoThreadSupport;
1539#endif
cristy3ed852e2009-09-05 21:47:34 +00001540#if defined(MAGICKCORE_JPEG_DELEGATE)
1541 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1542 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1543#endif
1544 entry->magick=(IsImageFormatHandler *) IsJPEG;
1545 entry->adjoin=MagickFalse;
1546 entry->description=ConstantString(description);
1547 if (*version != '\0')
1548 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001549 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001550 entry->module=ConstantString("JPEG");
1551 (void) RegisterMagickInfo(entry);
1552 entry=SetMagickInfo("JPG");
cristy8e62a802014-12-27 22:43:35 +00001553#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001554 entry->thread_support=NoThreadSupport;
1555#endif
cristy3ed852e2009-09-05 21:47:34 +00001556#if defined(MAGICKCORE_JPEG_DELEGATE)
1557 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1558 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1559#endif
1560 entry->adjoin=MagickFalse;
1561 entry->description=ConstantString(description);
1562 if (*version != '\0')
1563 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001564 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001565 entry->module=ConstantString("JPEG");
1566 (void) RegisterMagickInfo(entry);
1567 entry=SetMagickInfo("PJPEG");
cristy8e62a802014-12-27 22:43:35 +00001568#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001569 entry->thread_support=NoThreadSupport;
1570#endif
cristy3ed852e2009-09-05 21:47:34 +00001571#if defined(MAGICKCORE_JPEG_DELEGATE)
1572 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1573 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1574#endif
1575 entry->adjoin=MagickFalse;
1576 entry->description=ConstantString(description);
1577 if (*version != '\0')
1578 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001579 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001580 entry->module=ConstantString("JPEG");
1581 (void) RegisterMagickInfo(entry);
1582 return(MagickImageCoderSignature);
1583}
dirk29dd80e2013-10-31 23:11:11 +00001584
1585
cristy3ed852e2009-09-05 21:47:34 +00001586/*
1587%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1588% %
1589% %
1590% %
1591% U n r e g i s t e r J P E G I m a g e %
1592% %
1593% %
1594% %
1595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1596%
1597% UnregisterJPEGImage() removes format registrations made by the
1598% JPEG module from the list of supported formats.
1599%
1600% The format of the UnregisterJPEGImage method is:
1601%
1602% UnregisterJPEGImage(void)
1603%
1604*/
1605ModuleExport void UnregisterJPEGImage(void)
1606{
1607 (void) UnregisterMagickInfo("PJPG");
1608 (void) UnregisterMagickInfo("JPEG");
1609 (void) UnregisterMagickInfo("JPG");
cristyb338b712014-11-27 14:06:34 +00001610 (void) UnregisterMagickInfo("JPE");
cristy3ed852e2009-09-05 21:47:34 +00001611}
dirk29dd80e2013-10-31 23:11:11 +00001612
1613
cristy3ed852e2009-09-05 21:47:34 +00001614#if defined(MAGICKCORE_JPEG_DELEGATE)
1615/*
1616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617% %
1618% %
1619% %
1620% W r i t e J P E G I m a g e %
1621% %
1622% %
1623% %
1624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625%
1626% WriteJPEGImage() writes a JPEG image file and returns it. It
1627% allocates the memory necessary for the new Image structure and returns a
1628% pointer to the new image.
1629%
1630% The format of the WriteJPEGImage method is:
1631%
cristy91044972011-04-22 14:21:16 +00001632% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001633% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001634%
1635% A description of each parameter follows:
1636%
1637% o image_info: the image info.
1638%
1639% o jpeg_image: The image.
1640%
cristy1e178e72011-08-28 19:44:34 +00001641% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001642%
1643*/
1644
cristy1b58f252012-03-01 01:41:41 +00001645static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1646{
1647 assert(table != (QuantizationTable *) NULL);
1648 if (table->slot != (char *) NULL)
1649 table->slot=DestroyString(table->slot);
1650 if (table->description != (char *) NULL)
1651 table->description=DestroyString(table->description);
1652 if (table->levels != (unsigned int *) NULL)
1653 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1654 table=(QuantizationTable *) RelinquishMagickMemory(table);
1655 return(table);
1656}
1657
cristy3ed852e2009-09-05 21:47:34 +00001658static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1659{
1660 DestinationManager
1661 *destination;
1662
1663 destination=(DestinationManager *) cinfo->dest;
1664 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1665 MaxBufferExtent,destination->buffer);
1666 if (destination->manager.free_in_buffer != MaxBufferExtent)
1667 ERREXIT(cinfo,JERR_FILE_WRITE);
1668 destination->manager.next_output_byte=destination->buffer;
1669 return(TRUE);
1670}
1671
cristy1b58f252012-03-01 01:41:41 +00001672static QuantizationTable *GetQuantizationTable(const char *filename,
1673 const char *slot,ExceptionInfo *exception)
1674{
1675 char
1676 *p,
1677 *xml;
1678
1679 const char
1680 *attribute,
1681 *content;
1682
1683 double
1684 value;
1685
1686 register ssize_t
1687 i;
1688
1689 ssize_t
1690 j;
1691
1692 QuantizationTable
1693 *table;
1694
cristy1fcc8b62012-03-02 17:25:55 +00001695 size_t
1696 length;
1697
cristy1b58f252012-03-01 01:41:41 +00001698 XMLTreeInfo
1699 *description,
1700 *levels,
1701 *quantization_tables,
1702 *table_iterator;
1703
1704 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1705 "Loading quantization tables \"%s\" ...",filename);
1706 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001707 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001708 if (xml == (char *) NULL)
1709 return(table);
1710 quantization_tables=NewXMLTree(xml,exception);
1711 if (quantization_tables == (XMLTreeInfo *) NULL)
1712 {
1713 xml=DestroyString(xml);
1714 return(table);
1715 }
1716 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1717 table_iterator != (XMLTreeInfo *) NULL;
1718 table_iterator=GetNextXMLTreeTag(table_iterator))
1719 {
1720 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1721 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1722 break;
1723 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1724 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1725 break;
1726 }
1727 if (table_iterator == (XMLTreeInfo *) NULL)
1728 {
1729 xml=DestroyString(xml);
1730 return(table);
1731 }
1732 description=GetXMLTreeChild(table_iterator,"description");
1733 if (description == (XMLTreeInfo *) NULL)
1734 {
1735 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1736 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1737 quantization_tables=DestroyXMLTree(quantization_tables);
1738 xml=DestroyString(xml);
1739 return(table);
1740 }
1741 levels=GetXMLTreeChild(table_iterator,"levels");
1742 if (levels == (XMLTreeInfo *) NULL)
1743 {
1744 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1745 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1746 quantization_tables=DestroyXMLTree(quantization_tables);
1747 xml=DestroyString(xml);
1748 return(table);
1749 }
1750 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1751 if (table == (QuantizationTable *) NULL)
1752 ThrowFatalException(ResourceLimitFatalError,
1753 "UnableToAcquireQuantizationTable");
1754 table->slot=(char *) NULL;
1755 table->description=(char *) NULL;
1756 table->levels=(unsigned int *) NULL;
1757 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1758 if (attribute != (char *) NULL)
1759 table->slot=ConstantString(attribute);
1760 content=GetXMLTreeContent(description);
1761 if (content != (char *) NULL)
1762 table->description=ConstantString(content);
1763 attribute=GetXMLTreeAttribute(levels,"width");
1764 if (attribute == (char *) NULL)
1765 {
1766 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1767 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1768 quantization_tables=DestroyXMLTree(quantization_tables);
1769 table=DestroyQuantizationTable(table);
1770 xml=DestroyString(xml);
1771 return(table);
1772 }
1773 table->width=StringToUnsignedLong(attribute);
1774 if (table->width == 0)
1775 {
1776 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1777 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1778 quantization_tables=DestroyXMLTree(quantization_tables);
1779 table=DestroyQuantizationTable(table);
1780 xml=DestroyString(xml);
1781 return(table);
1782 }
1783 attribute=GetXMLTreeAttribute(levels,"height");
1784 if (attribute == (char *) NULL)
1785 {
1786 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1787 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1788 quantization_tables=DestroyXMLTree(quantization_tables);
1789 table=DestroyQuantizationTable(table);
1790 xml=DestroyString(xml);
1791 return(table);
1792 }
1793 table->height=StringToUnsignedLong(attribute);
1794 if (table->height == 0)
1795 {
1796 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1797 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1798 quantization_tables=DestroyXMLTree(quantization_tables);
1799 table=DestroyQuantizationTable(table);
1800 xml=DestroyString(xml);
1801 return(table);
1802 }
1803 attribute=GetXMLTreeAttribute(levels,"divisor");
1804 if (attribute == (char *) NULL)
1805 {
1806 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1807 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1808 quantization_tables=DestroyXMLTree(quantization_tables);
1809 table=DestroyQuantizationTable(table);
1810 xml=DestroyString(xml);
1811 return(table);
1812 }
cristy043f3f32012-03-02 17:37:28 +00001813 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1814 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001815 {
1816 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1817 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1818 quantization_tables=DestroyXMLTree(quantization_tables);
1819 table=DestroyQuantizationTable(table);
1820 xml=DestroyString(xml);
1821 return(table);
1822 }
1823 content=GetXMLTreeContent(levels);
1824 if (content == (char *) NULL)
1825 {
1826 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1827 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1828 quantization_tables=DestroyXMLTree(quantization_tables);
1829 table=DestroyQuantizationTable(table);
1830 xml=DestroyString(xml);
1831 return(table);
1832 }
cristy1fcc8b62012-03-02 17:25:55 +00001833 length=(size_t) table->width*table->height;
1834 if (length < 64)
1835 length=64;
cristy092006b2012-03-02 17:26:02 +00001836 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001837 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001838 if (table->levels == (unsigned int *) NULL)
1839 ThrowFatalException(ResourceLimitFatalError,
1840 "UnableToAcquireQuantizationTable");
1841 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1842 {
cristy043f3f32012-03-02 17:37:28 +00001843 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1844 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001845 while (isspace((int) ((unsigned char) *p)) != 0)
1846 p++;
1847 if (*p == ',')
1848 p++;
1849 content=p;
1850 }
cristy043f3f32012-03-02 17:37:28 +00001851 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001852 (void) value;
1853 if (p != content)
1854 {
1855 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1856 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1857 quantization_tables=DestroyXMLTree(quantization_tables);
1858 table=DestroyQuantizationTable(table);
1859 xml=DestroyString(xml);
1860 return(table);
1861 }
cristy043f3f32012-03-02 17:37:28 +00001862 for (j=i; j < 64; j++)
1863 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001864 quantization_tables=DestroyXMLTree(quantization_tables);
1865 xml=DestroyString(xml);
1866 return(table);
1867}
1868
cristy3ed852e2009-09-05 21:47:34 +00001869static void InitializeDestination(j_compress_ptr cinfo)
1870{
1871 DestinationManager
1872 *destination;
1873
1874 destination=(DestinationManager *) cinfo->dest;
1875 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1876 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1877 destination->manager.next_output_byte=destination->buffer;
1878 destination->manager.free_in_buffer=MaxBufferExtent;
1879}
1880
cristy3ed852e2009-09-05 21:47:34 +00001881static void TerminateDestination(j_compress_ptr cinfo)
1882{
1883 DestinationManager
1884 *destination;
1885
1886 destination=(DestinationManager *) cinfo->dest;
1887 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1888 {
1889 ssize_t
1890 count;
1891
1892 count=WriteBlob(destination->image,MaxBufferExtent-
1893 destination->manager.free_in_buffer,destination->buffer);
1894 if (count != (ssize_t)
1895 (MaxBufferExtent-destination->manager.free_in_buffer))
1896 ERREXIT(cinfo,JERR_FILE_WRITE);
1897 }
1898}
1899
1900static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1901{
1902 const char
1903 *name;
1904
1905 const StringInfo
1906 *profile;
1907
1908 MagickBooleanType
1909 iptc;
1910
cristybb503372010-05-27 20:51:26 +00001911 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001912 i;
1913
1914 size_t
cristy524222d2011-04-25 00:37:06 +00001915 length,
1916 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001917
1918 StringInfo
1919 *custom_profile;
1920
cristy3ed852e2009-09-05 21:47:34 +00001921 /*
1922 Save image profile as a APP marker.
1923 */
1924 iptc=MagickFalse;
1925 custom_profile=AcquireStringInfo(65535L);
1926 ResetImageProfileIterator(image);
1927 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1928 {
cristy109e5572010-09-16 18:38:17 +00001929 register unsigned char
1930 *p;
1931
cristy3ed852e2009-09-05 21:47:34 +00001932 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001933 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001934 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001935 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001936 {
1937 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1938 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1939 (unsigned int) length);
1940 }
1941 if (LocaleCompare(name,"ICC") == 0)
1942 {
1943 register unsigned char
1944 *p;
1945
cristy8ba82ae2013-05-29 14:19:49 +00001946 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001947 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001948 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001949 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001950 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001951 {
1952 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001953 p[12]=(unsigned char) ((i/65519L)+1);
1954 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001955 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001956 length);
1957 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001958 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001959 }
1960 }
1961 if (((LocaleCompare(name,"IPTC") == 0) ||
1962 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1963 {
cristybb503372010-05-27 20:51:26 +00001964 size_t
cristy3ed852e2009-09-05 21:47:34 +00001965 roundup;
1966
1967 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001968 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001969 {
1970 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001971 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001972 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1973 {
1974 (void) memcpy(p,"Photoshop 3.0 ",14);
1975 tag_length=14;
1976 }
1977 else
1978 {
1979 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1980 tag_length=26;
1981 p[24]=(unsigned char) (length >> 8);
1982 p[25]=(unsigned char) (length & 0xff);
1983 }
1984 p[13]=0x00;
1985 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001986 if (roundup != 0)
1987 p[length+tag_length]='\0';
1988 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1989 custom_profile),(unsigned int) (length+tag_length+roundup));
1990 }
1991 }
1992 if (LocaleCompare(name,"XMP") == 0)
1993 {
1994 StringInfo
1995 *xmp_profile;
1996
1997 /*
1998 Add namespace to XMP profile.
1999 */
cristy0615f0e2011-10-12 11:36:46 +00002000 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy30841e62014-05-19 00:45:15 +00002001 if (xmp_profile != (StringInfo *) NULL)
cristy8418c7e2014-05-18 18:38:26 +00002002 {
cristyc08a0aa2014-05-25 23:30:09 +00002003 if (profile != (StringInfo *) NULL)
2004 ConcatenateStringInfo(xmp_profile,profile);
cristy8418c7e2014-05-18 18:38:26 +00002005 GetStringInfoDatum(xmp_profile)[28]='\0';
2006 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
2007 {
2008 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
2009 jpeg_write_marker(jpeg_info,XML_MARKER,
2010 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
2011 }
2012 xmp_profile=DestroyStringInfo(xmp_profile);
2013 }
cristy3ed852e2009-09-05 21:47:34 +00002014 }
cristye8c25f92010-06-03 00:53:06 +00002015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2016 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00002017 name=GetNextImageProfile(image);
2018 }
2019 custom_profile=DestroyStringInfo(custom_profile);
2020}
2021
2022static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
2023{
2024 DestinationManager
2025 *destination;
2026
2027 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
2028 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
2029 destination=(DestinationManager *) cinfo->dest;
2030 destination->manager.init_destination=InitializeDestination;
2031 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2032 destination->manager.term_destination=TerminateDestination;
2033 destination->image=image;
2034}
2035
2036static char **SamplingFactorToList(const char *text)
2037{
2038 char
2039 **textlist;
2040
2041 register char
2042 *q;
2043
2044 register const char
2045 *p;
2046
cristybb503372010-05-27 20:51:26 +00002047 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002048 i;
2049
cristybb503372010-05-27 20:51:26 +00002050 size_t
cristy3ed852e2009-09-05 21:47:34 +00002051 lines;
2052
2053 if (text == (char *) NULL)
2054 return((char **) NULL);
2055 /*
2056 Convert string to an ASCII list.
2057 */
2058 lines=1;
2059 for (p=text; *p != '\0'; p++)
2060 if (*p == ',')
2061 lines++;
2062 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
2063 sizeof(*textlist));
2064 if (textlist == (char **) NULL)
2065 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2066 p=text;
cristybb503372010-05-27 20:51:26 +00002067 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00002068 {
2069 for (q=(char *) p; *q != '\0'; q++)
2070 if (*q == ',')
2071 break;
2072 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
2073 sizeof(*textlist[i]));
2074 if (textlist[i] == (char *) NULL)
2075 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2076 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2077 if (*q == '\r')
2078 q++;
2079 p=q+1;
2080 }
2081 textlist[i]=(char *) NULL;
2082 return(textlist);
2083}
2084
2085static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002086 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002087{
2088 const char
2089 *option,
2090 *sampling_factor,
2091 *value;
2092
2093 ErrorManager
2094 error_manager;
2095
cristy21749e92012-02-18 02:29:21 +00002096 int
cristy7b8ed292013-05-18 01:13:56 +00002097 colorspace,
cristy21749e92012-02-18 02:29:21 +00002098 quality;
2099
cristy3ed852e2009-09-05 21:47:34 +00002100 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002101 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002102
2103 JSAMPROW
2104 scanline[1];
2105
cristy3ed852e2009-09-05 21:47:34 +00002106 MagickBooleanType
2107 status;
2108
cristy22646e22013-06-23 16:34:03 +00002109 MemoryInfo
2110 *memory_info;
2111
cristy3ed852e2009-09-05 21:47:34 +00002112 register JSAMPLE
2113 *q;
2114
cristybb503372010-05-27 20:51:26 +00002115 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002116 i;
2117
cristy524222d2011-04-25 00:37:06 +00002118 ssize_t
2119 y;
2120
cristy3ed852e2009-09-05 21:47:34 +00002121 struct jpeg_compress_struct
2122 jpeg_info;
2123
2124 struct jpeg_error_mgr
2125 jpeg_error;
2126
cristy5328a4c2013-12-03 11:32:13 +00002127 unsigned short
2128 scale;
2129
cristy3ed852e2009-09-05 21:47:34 +00002130 /*
2131 Open image file.
2132 */
2133 assert(image_info != (const ImageInfo *) NULL);
2134 assert(image_info->signature == MagickSignature);
2135 assert(image != (Image *) NULL);
2136 assert(image->signature == MagickSignature);
2137 if (image->debug != MagickFalse)
2138 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002139 assert(exception != (ExceptionInfo *) NULL);
2140 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00002141 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002142 if (status == MagickFalse)
2143 return(status);
2144 /*
2145 Initialize JPEG parameters.
2146 */
cristy91044972011-04-22 14:21:16 +00002147 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002148 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2149 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2150 jpeg_info.client_data=(void *) image;
2151 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002152 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002153 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002154 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002155 error_manager.image=image;
cristy72f87f82013-06-23 16:43:05 +00002156 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002157 if (setjmp(error_manager.error_recovery) != 0)
2158 {
2159 jpeg_destroy_compress(&jpeg_info);
2160 (void) CloseBlob(image);
2161 return(MagickFalse);
2162 }
2163 jpeg_info.client_data=(void *) &error_manager;
2164 jpeg_create_compress(&jpeg_info);
2165 JPEGDestinationManager(&jpeg_info,image);
2166 if ((image->columns != (unsigned int) image->columns) ||
2167 (image->rows != (unsigned int) image->rows))
2168 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2169 jpeg_info.image_width=(unsigned int) image->columns;
2170 jpeg_info.image_height=(unsigned int) image->rows;
2171 jpeg_info.input_components=3;
2172 jpeg_info.data_precision=8;
2173 jpeg_info.in_color_space=JCS_RGB;
2174 switch (image->colorspace)
2175 {
2176 case CMYKColorspace:
2177 {
2178 jpeg_info.input_components=4;
2179 jpeg_info.in_color_space=JCS_CMYK;
2180 break;
2181 }
2182 case YCbCrColorspace:
2183 case Rec601YCbCrColorspace:
2184 case Rec709YCbCrColorspace:
2185 {
2186 jpeg_info.in_color_space=JCS_YCbCr;
2187 break;
2188 }
2189 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002190 {
cristye9355552014-01-16 13:35:05 +00002191 if (image_info->type == TrueColorType)
2192 break;
cristy3ed852e2009-09-05 21:47:34 +00002193 jpeg_info.input_components=1;
2194 jpeg_info.in_color_space=JCS_GRAYSCALE;
2195 break;
2196 }
2197 default:
cristy9f396782009-12-21 01:37:45 +00002198 {
cristyaf8d3912014-02-21 14:50:33 +00002199 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye9355552014-01-16 13:35:05 +00002200 if (image_info->type == TrueColorType)
2201 break;
cristyc47ba2d2014-01-16 19:41:18 +00002202 if (IsImageGray(image,exception) != MagickFalse)
cristye9355552014-01-16 13:35:05 +00002203 {
2204 jpeg_info.input_components=1;
2205 jpeg_info.in_color_space=JCS_GRAYSCALE;
2206 }
cristy3ed852e2009-09-05 21:47:34 +00002207 break;
cristy9f396782009-12-21 01:37:45 +00002208 }
cristy3ed852e2009-09-05 21:47:34 +00002209 }
cristy3ed852e2009-09-05 21:47:34 +00002210 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002211 if (jpeg_info.in_color_space == JCS_CMYK)
2212 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002213 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2214 jpeg_info.data_precision=8;
2215 else
dirk93b02b72013-11-16 16:03:36 +00002216 jpeg_info.data_precision=BITS_IN_JSAMPLE;
cristy4d30a482014-10-19 12:58:39 +00002217 jpeg_info.density_unit=1;
cristy3ed852e2009-09-05 21:47:34 +00002218 if (image->debug != MagickFalse)
2219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002220 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2221 floor(image->resolution.y+0.5));
2222 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002223 {
2224 /*
2225 Set image resolution.
2226 */
cristydf878012014-01-12 23:39:40 +00002227 jpeg_info.write_JFIF_header=TRUE;
cristy2a11bef2011-10-28 18:33:11 +00002228 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2229 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy0c220632013-09-25 22:10:28 +00002230 /*
2231 Set image resolution units.
2232 */
2233 jpeg_info.density_unit=(UINT8) 0;
cristy3ed852e2009-09-05 21:47:34 +00002234 if (image->units == PixelsPerInchResolution)
2235 jpeg_info.density_unit=(UINT8) 1;
2236 if (image->units == PixelsPerCentimeterResolution)
2237 jpeg_info.density_unit=(UINT8) 2;
2238 }
cristy97cb3bf2012-02-21 18:31:20 +00002239 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002240 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002241 if (option != (const char *) NULL)
2242 switch (*option)
2243 {
2244 case 'D':
2245 case 'd':
2246 {
2247 if (LocaleCompare(option,"default") == 0)
2248 jpeg_info.dct_method=JDCT_DEFAULT;
2249 break;
2250 }
2251 case 'F':
2252 case 'f':
2253 {
2254 if (LocaleCompare(option,"fastest") == 0)
2255 jpeg_info.dct_method=JDCT_FASTEST;
2256 if (LocaleCompare(option,"float") == 0)
2257 jpeg_info.dct_method=JDCT_FLOAT;
2258 break;
2259 }
2260 case 'I':
2261 case 'i':
2262 {
2263 if (LocaleCompare(option,"ifast") == 0)
2264 jpeg_info.dct_method=JDCT_IFAST;
2265 if (LocaleCompare(option,"islow") == 0)
2266 jpeg_info.dct_method=JDCT_ISLOW;
2267 break;
2268 }
2269 }
cristy092ec8d2013-04-26 13:46:22 +00002270 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002271 if (option != (const char *) NULL)
cristyb9d0e7e2014-11-02 18:14:44 +00002272 jpeg_info.optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE :
2273 FALSE;
cristy7d31f6e2014-11-11 13:07:58 +00002274 else
2275 {
2276 MagickSizeType
2277 length;
2278
2279 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2280 image->rows*sizeof(JSAMPLE);
2281 if (length == (MagickSizeType) ((size_t) length))
2282 {
2283 /*
2284 Perform optimization only if available memory resources permit it.
2285 */
2286 status=AcquireMagickResource(MemoryResource,length);
2287 RelinquishMagickResource(MemoryResource,length);
2288 jpeg_info.optimize_coding=status == MagickFalse ? FALSE : TRUE;
2289 }
2290 }
cristy3ed852e2009-09-05 21:47:34 +00002291#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2292 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2293 (image_info->interlace != NoInterlace))
2294 {
2295 if (image->debug != MagickFalse)
2296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2297 "Interlace: progressive");
2298 jpeg_simple_progression(&jpeg_info);
2299 }
2300 else
2301 if (image->debug != MagickFalse)
2302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2303 "Interlace: non-progressive");
2304#else
2305 if (image->debug != MagickFalse)
2306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2307 "Interlace: nonprogressive");
2308#endif
dirk871abab2013-11-03 13:21:16 +00002309 quality=92;
cristy2a950402014-03-12 17:57:14 +00002310 if ((image_info->compression != LosslessJPEGCompression) &&
2311 (image->quality <= 100))
2312 {
2313 if (image->quality != UndefinedCompressionQuality)
2314 quality=(int) image->quality;
2315 if (image->debug != MagickFalse)
2316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2317 (double) image->quality);
2318 }
2319 else
2320 {
2321#if !defined(C_LOSSLESS_SUPPORTED)
2322 quality=100;
2323 if (image->debug != MagickFalse)
2324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2325#else
2326 if (image->quality < 100)
2327 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2328 "LosslessToLossyJPEGConversion",image->filename);
2329 else
2330 {
2331 int
2332 point_transform,
2333 predictor;
2334
2335 predictor=image->quality/100; /* range 1-7 */
2336 point_transform=image->quality % 20; /* range 0-15 */
2337 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2338 if (image->debug != MagickFalse)
2339 {
2340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2341 "Compression: lossless");
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 "Predictor: %d",predictor);
2344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2345 "Point Transform: %d",point_transform);
2346 }
2347 }
2348#endif
2349 }
cristy092ec8d2013-04-26 13:46:22 +00002350 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002351 if (option != (const char *) NULL)
2352 {
2353 Image
2354 *jpeg_image;
2355
2356 ImageInfo
2357 *jpeg_info;
2358
2359 jpeg_info=CloneImageInfo(image_info);
cristye0823d52013-11-03 21:50:10 +00002360 jpeg_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002361 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002362 if (jpeg_image != (Image *) NULL)
2363 {
2364 MagickSizeType
2365 extent;
2366
2367 size_t
cristy87e73ab2010-02-12 01:59:12 +00002368 maximum,
2369 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002370
2371 /*
2372 Search for compression quality that does not exceed image extent.
2373 */
2374 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002375 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002376 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002377 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
cristye0a316c2010-02-13 19:15:23 +00002378 maximum=101;
dirkcf845242013-08-11 21:36:19 +00002379 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002380 {
cristy3975f402014-02-26 14:36:33 +00002381 (void) AcquireUniqueFilename(jpeg_image->filename);
2382 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
cristy1e178e72011-08-28 19:44:34 +00002383 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002384 if (GetBlobSize(jpeg_image) <= extent)
cristy3975f402014-02-26 14:36:33 +00002385 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002386 else
cristy3975f402014-02-26 14:36:33 +00002387 maximum=jpeg_image->quality-1;
2388 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristy0adb4f92009-11-28 18:08:51 +00002389 }
dirk39c51eb2014-12-27 01:01:27 +00002390 quality=(int)minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002391 jpeg_image=DestroyImage(jpeg_image);
2392 }
2393 jpeg_info=DestroyImageInfo(jpeg_info);
2394 }
cristy9bb7c842014-06-17 23:42:24 +00002395 jpeg_set_quality(&jpeg_info,quality,TRUE);
cristy16e74722012-07-01 13:01:38 +00002396#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002397 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002398 if (option != (const char *) NULL)
2399 {
2400 GeometryInfo
2401 geometry_info;
2402
2403 int
2404 flags;
2405
2406 /*
2407 Set quality scaling for luminance and chrominance separately.
2408 */
2409 flags=ParseGeometry(option,&geometry_info);
2410 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2411 {
2412 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2413 (geometry_info.rho+0.5));
2414 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2415 (geometry_info.sigma+0.5));
cristy9bb7c842014-06-17 23:42:24 +00002416 jpeg_default_qtables(&jpeg_info,TRUE);
cristy16e74722012-07-01 13:01:38 +00002417 }
2418 }
2419#endif
cristy7b8ed292013-05-18 01:13:56 +00002420 colorspace=jpeg_info.in_color_space;
2421 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002422 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002423 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002424 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002425 colorspace=StringToInteger(value);
2426 sampling_factor=(const char *) NULL;
2427 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002428 {
cristy7b8ed292013-05-18 01:13:56 +00002429 value=GetImageOption(image_info,"jpeg:sampling-factor");
2430 if (value == (char *) NULL)
2431 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2432 if (value != (char *) NULL)
2433 {
2434 sampling_factor=value;
2435 if (image->debug != MagickFalse)
2436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2437 " Input sampling-factors=%s",sampling_factor);
2438 }
cristy3ed852e2009-09-05 21:47:34 +00002439 }
cristy7b8ed292013-05-18 01:13:56 +00002440 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002441 if (image_info->sampling_factor != (char *) NULL)
2442 sampling_factor=image_info->sampling_factor;
2443 if (sampling_factor == (const char *) NULL)
2444 {
2445 if (image->quality >= 90)
2446 for (i=0; i < MAX_COMPONENTS; i++)
2447 {
2448 jpeg_info.comp_info[i].h_samp_factor=1;
2449 jpeg_info.comp_info[i].v_samp_factor=1;
2450 }
2451 }
2452 else
2453 {
2454 char
2455 **factors;
2456
2457 GeometryInfo
2458 geometry_info;
2459
2460 MagickStatusType
2461 flags;
2462
2463 /*
2464 Set sampling factor.
2465 */
2466 i=0;
2467 factors=SamplingFactorToList(sampling_factor);
2468 if (factors != (char **) NULL)
2469 {
2470 for (i=0; i < MAX_COMPONENTS; i++)
2471 {
2472 if (factors[i] == (char *) NULL)
2473 break;
2474 flags=ParseGeometry(factors[i],&geometry_info);
2475 if ((flags & SigmaValue) == 0)
2476 geometry_info.sigma=geometry_info.rho;
2477 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2478 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2479 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2480 }
2481 factors=(char **) RelinquishMagickMemory(factors);
2482 }
2483 for ( ; i < MAX_COMPONENTS; i++)
2484 {
2485 jpeg_info.comp_info[i].h_samp_factor=1;
2486 jpeg_info.comp_info[i].v_samp_factor=1;
2487 }
2488 }
cristy092ec8d2013-04-26 13:46:22 +00002489 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002490 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002491 {
cristy1b58f252012-03-01 01:41:41 +00002492 QuantizationTable
2493 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002494
cristy1445b322012-02-19 18:57:30 +00002495 /*
cristy1b58f252012-03-01 01:41:41 +00002496 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002497 */
cristy1b58f252012-03-01 01:41:41 +00002498 table=GetQuantizationTable(option,"0",exception);
2499 if (table != (QuantizationTable *) NULL)
2500 {
cristyae453b72013-04-21 22:24:20 +00002501 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002502 jpeg_info.comp_info[i].quant_tbl_no=0;
2503 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2504 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002505 table=DestroyQuantizationTable(table);
2506 }
2507 table=GetQuantizationTable(option,"1",exception);
2508 if (table != (QuantizationTable *) NULL)
2509 {
cristyae453b72013-04-21 22:24:20 +00002510 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002511 jpeg_info.comp_info[i].quant_tbl_no=1;
2512 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2513 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002514 table=DestroyQuantizationTable(table);
2515 }
2516 table=GetQuantizationTable(option,"2",exception);
2517 if (table != (QuantizationTable *) NULL)
2518 {
cristyae453b72013-04-21 22:24:20 +00002519 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002520 jpeg_info.comp_info[i].quant_tbl_no=2;
2521 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2522 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002523 table=DestroyQuantizationTable(table);
2524 }
2525 table=GetQuantizationTable(option,"3",exception);
2526 if (table != (QuantizationTable *) NULL)
2527 {
cristyae453b72013-04-21 22:24:20 +00002528 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002529 jpeg_info.comp_info[i].quant_tbl_no=3;
2530 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2531 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002532 table=DestroyQuantizationTable(table);
2533 }
cristy1445b322012-02-19 18:57:30 +00002534 }
cristy9bb7c842014-06-17 23:42:24 +00002535 jpeg_start_compress(&jpeg_info,TRUE);
cristy3ed852e2009-09-05 21:47:34 +00002536 if (image->debug != MagickFalse)
2537 {
2538 if (image->storage_class == PseudoClass)
2539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2540 "Storage class: PseudoClass");
2541 else
2542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2543 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2545 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002546 if (image->colors != 0)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002548 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002549 else
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 "Number of colors: unspecified");
2552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2553 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2554 switch (image->colorspace)
2555 {
2556 case CMYKColorspace:
2557 {
2558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2559 "Storage class: DirectClass");
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2561 "Colorspace: CMYK");
2562 break;
2563 }
2564 case YCbCrColorspace:
2565 case Rec601YCbCrColorspace:
2566 case Rec709YCbCrColorspace:
2567 {
2568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2569 "Colorspace: YCbCr");
2570 break;
2571 }
2572 default:
2573 break;
2574 }
2575 switch (image->colorspace)
2576 {
2577 case CMYKColorspace:
2578 {
2579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2580 "Colorspace: CMYK");
2581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2583 jpeg_info.comp_info[0].h_samp_factor,
2584 jpeg_info.comp_info[0].v_samp_factor,
2585 jpeg_info.comp_info[1].h_samp_factor,
2586 jpeg_info.comp_info[1].v_samp_factor,
2587 jpeg_info.comp_info[2].h_samp_factor,
2588 jpeg_info.comp_info[2].v_samp_factor,
2589 jpeg_info.comp_info[3].h_samp_factor,
2590 jpeg_info.comp_info[3].v_samp_factor);
2591 break;
2592 }
2593 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002594 {
2595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2596 "Colorspace: GRAY");
2597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2598 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2599 jpeg_info.comp_info[0].v_samp_factor);
2600 break;
2601 }
cristyed8d7852013-08-01 22:44:22 +00002602 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002603 case RGBColorspace:
2604 {
2605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002606 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2608 "Sampling factors: %dx%d,%dx%d,%dx%d",
2609 jpeg_info.comp_info[0].h_samp_factor,
2610 jpeg_info.comp_info[0].v_samp_factor,
2611 jpeg_info.comp_info[1].h_samp_factor,
2612 jpeg_info.comp_info[1].v_samp_factor,
2613 jpeg_info.comp_info[2].h_samp_factor,
2614 jpeg_info.comp_info[2].v_samp_factor);
2615 break;
2616 }
2617 case YCbCrColorspace:
2618 case Rec601YCbCrColorspace:
2619 case Rec709YCbCrColorspace:
2620 {
2621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2622 "Colorspace: YCbCr");
2623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2624 "Sampling factors: %dx%d,%dx%d,%dx%d",
2625 jpeg_info.comp_info[0].h_samp_factor,
2626 jpeg_info.comp_info[0].v_samp_factor,
2627 jpeg_info.comp_info[1].h_samp_factor,
2628 jpeg_info.comp_info[1].v_samp_factor,
2629 jpeg_info.comp_info[2].h_samp_factor,
2630 jpeg_info.comp_info[2].v_samp_factor);
2631 break;
2632 }
2633 default:
2634 {
2635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2636 image->colorspace);
2637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2638 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2639 jpeg_info.comp_info[0].h_samp_factor,
2640 jpeg_info.comp_info[0].v_samp_factor,
2641 jpeg_info.comp_info[1].h_samp_factor,
2642 jpeg_info.comp_info[1].v_samp_factor,
2643 jpeg_info.comp_info[2].h_samp_factor,
2644 jpeg_info.comp_info[2].v_samp_factor,
2645 jpeg_info.comp_info[3].h_samp_factor,
2646 jpeg_info.comp_info[3].v_samp_factor);
2647 break;
2648 }
2649 }
2650 }
2651 /*
2652 Write JPEG profiles.
2653 */
cristyd15e6592011-10-15 00:13:06 +00002654 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002655 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002656 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002657 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2658 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2659 if (image->profiles != (void *) NULL)
2660 WriteProfile(&jpeg_info,image);
2661 /*
2662 Convert MIFF to JPEG raster pixels.
2663 */
cristy22646e22013-06-23 16:34:03 +00002664 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002665 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002666 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002667 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002668 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002669 if (setjmp(error_manager.error_recovery) != 0)
2670 {
2671 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002672 if (memory_info != (MemoryInfo *) NULL)
2673 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002674 (void) CloseBlob(image);
2675 return(MagickFalse);
2676 }
2677 scanline[0]=(JSAMPROW) jpeg_pixels;
cristy477b9fd2013-12-03 16:52:31 +00002678 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy31db6c12013-12-04 15:17:30 +00002679 if (scale == 0)
2680 scale=1;
cristye90d7402010-03-14 18:21:29 +00002681 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002682 {
cristy3ed852e2009-09-05 21:47:34 +00002683 if ((jpeg_info.in_color_space == JCS_RGB) ||
2684 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002685 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002686 {
cristy4c08aed2011-07-01 19:47:50 +00002687 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002688 *p;
2689
cristybb503372010-05-27 20:51:26 +00002690 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002691 x;
2692
cristy1e178e72011-08-28 19:44:34 +00002693 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002694 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002695 break;
2696 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002697 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002698 {
cristy4c08aed2011-07-01 19:47:50 +00002699 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2700 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2701 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002702 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002703 }
2704 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002705 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2706 image->rows);
2707 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002708 break;
2709 }
2710 else
cristye90d7402010-03-14 18:21:29 +00002711 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002712 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002713 {
cristyd0323222013-04-07 16:13:21 +00002714 register const Quantum
2715 *p;
2716
2717 register ssize_t
2718 x;
2719
2720 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2721 if (p == (const Quantum *) NULL)
2722 break;
2723 q=jpeg_pixels;
2724 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002725 {
cristyd0323222013-04-07 16:13:21 +00002726 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2727 image,p)));
2728 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002729 }
cristyd0323222013-04-07 16:13:21 +00002730 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2731 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2732 image->rows);
2733 if (status == MagickFalse)
2734 break;
2735 }
cristye90d7402010-03-14 18:21:29 +00002736 else
cristybb503372010-05-27 20:51:26 +00002737 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002738 {
cristy4c08aed2011-07-01 19:47:50 +00002739 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002740 *p;
2741
cristybb503372010-05-27 20:51:26 +00002742 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002743 x;
2744
cristy1e178e72011-08-28 19:44:34 +00002745 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002746 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002747 break;
2748 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002749 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002750 {
2751 /*
2752 Convert DirectClass packets to contiguous CMYK scanlines.
2753 */
2754 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002755 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002756 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002757 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002758 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002759 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002760 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002761 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002762 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002763 }
2764 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002765 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2766 image->rows);
2767 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002768 break;
2769 }
2770 }
2771 else
2772 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002773 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002774 {
cristy4c08aed2011-07-01 19:47:50 +00002775 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002776 *p;
2777
cristybb503372010-05-27 20:51:26 +00002778 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002779 x;
2780
cristy1e178e72011-08-28 19:44:34 +00002781 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002782 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002783 break;
2784 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002785 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002786 {
cristy217ddd62013-12-06 23:27:09 +00002787 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2788 p)))/scale);
cristyed231572011-07-14 02:18:59 +00002789 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002790 }
2791 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002792 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2793 image->rows);
2794 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002795 break;
2796 }
2797 else
2798 if ((jpeg_info.in_color_space == JCS_RGB) ||
2799 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002800 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002801 {
cristy4c08aed2011-07-01 19:47:50 +00002802 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002803 *p;
2804
cristybb503372010-05-27 20:51:26 +00002805 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002806 x;
2807
cristy1e178e72011-08-28 19:44:34 +00002808 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002809 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002810 break;
2811 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002812 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002813 {
cristy5328a4c2013-12-03 11:32:13 +00002814 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2815 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2816 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002817 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002818 }
2819 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002820 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002821 image->rows);
cristye90d7402010-03-14 18:21:29 +00002822 if (status == MagickFalse)
2823 break;
2824 }
2825 else
cristybb503372010-05-27 20:51:26 +00002826 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002827 {
cristy4c08aed2011-07-01 19:47:50 +00002828 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002829 *p;
2830
cristybb503372010-05-27 20:51:26 +00002831 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002832 x;
2833
cristy1e178e72011-08-28 19:44:34 +00002834 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002835 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002836 break;
2837 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002838 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002839 {
2840 /*
2841 Convert DirectClass packets to contiguous CMYK scanlines.
2842 */
cristy217ddd62013-12-06 23:27:09 +00002843 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2844 image,p))/scale);
2845 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2846 image,p))/scale);
2847 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2848 image,p))/scale);
2849 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2850 image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002851 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002852 }
2853 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002854 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002855 image->rows);
cristye90d7402010-03-14 18:21:29 +00002856 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002857 break;
2858 }
cristybb503372010-05-27 20:51:26 +00002859 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002860 jpeg_finish_compress(&jpeg_info);
2861 /*
2862 Relinquish resources.
2863 */
2864 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002865 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002866 (void) CloseBlob(image);
2867 return(MagickTrue);
2868}
2869#endif