blob: 47944ae773517959443c0664b6facb436e56afec [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
cristy86b6e552015-06-06 13:25:16 +000098
cristy3ed852e2009-09-05 21:47:34 +000099/*
100 Define declarations.
101*/
102#define ICC_MARKER (JPEG_APP0+2)
103#define ICC_PROFILE "ICC_PROFILE"
104#define IPTC_MARKER (JPEG_APP0+13)
105#define XML_MARKER (JPEG_APP0+1)
cristyeb9759e2012-06-07 23:06:29 +0000106#define MaxBufferExtent 16384
cristy86b6e552015-06-06 13:25:16 +0000107
cristy3ed852e2009-09-05 21:47:34 +0000108/*
109 Typedef declarations.
110*/
111#if defined(MAGICKCORE_JPEG_DELEGATE)
112typedef struct _DestinationManager
113{
114 struct jpeg_destination_mgr
115 manager;
116
117 Image
118 *image;
119
120 JOCTET
121 *buffer;
122} DestinationManager;
123
124typedef struct _ErrorManager
125{
cristy018f07f2011-09-04 21:15:19 +0000126 ExceptionInfo
127 *exception;
128
cristy3ed852e2009-09-05 21:47:34 +0000129 Image
130 *image;
131
cristyd28b1dd2011-05-14 20:30:38 +0000132 MagickBooleanType
133 finished;
134
cristyf551aee2012-09-27 21:45:23 +0000135 StringInfo
136 *profile;
137
cristy3ed852e2009-09-05 21:47:34 +0000138 jmp_buf
139 error_recovery;
140} ErrorManager;
141
142typedef struct _SourceManager
143{
144 struct jpeg_source_mgr
145 manager;
146
147 Image
148 *image;
149
150 JOCTET
151 *buffer;
152
153 boolean
154 start_of_blob;
155} SourceManager;
156#endif
cristy1b58f252012-03-01 01:41:41 +0000157
158typedef struct _QuantizationTable
159{
160 char
161 *slot,
162 *description;
163
164 size_t
165 width,
166 height;
167
cristy043f3f32012-03-02 17:37:28 +0000168 double
169 divisor;
170
cristy1b58f252012-03-01 01:41:41 +0000171 unsigned int
cristy1b58f252012-03-01 01:41:41 +0000172 *levels;
173} QuantizationTable;
cristy86b6e552015-06-06 13:25:16 +0000174
cristy3ed852e2009-09-05 21:47:34 +0000175/*
176 Forward declarations.
177*/
178#if defined(MAGICKCORE_JPEG_DELEGATE)
179static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000180 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000181#endif
cristy86b6e552015-06-06 13:25:16 +0000182
cristy3ed852e2009-09-05 21:47:34 +0000183/*
184%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
185% %
186% %
187% %
188% I s J P E G %
189% %
190% %
191% %
192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193%
194% IsJPEG() returns MagickTrue if the image format type, identified by the
195% magick string, is JPEG.
196%
197% The format of the IsJPEG method is:
198%
199% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
200%
201% A description of each parameter follows:
202%
203% o magick: compare image format pattern against these bytes.
204%
205% o length: Specifies the length of the magick string.
206%
207*/
208static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
209{
210 if (length < 3)
211 return(MagickFalse);
212 if (memcmp(magick,"\377\330\377",3) == 0)
213 return(MagickTrue);
214 return(MagickFalse);
215}
cristy86b6e552015-06-06 13:25:16 +0000216
cristy3ed852e2009-09-05 21:47:34 +0000217#if defined(MAGICKCORE_JPEG_DELEGATE)
218/*
219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220% %
221% %
222% %
223% R e a d J P E G I m a g e %
224% %
225% %
226% %
227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228%
229% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
230% the memory necessary for the new Image structure and returns a pointer to
231% the new image.
232%
233% The format of the ReadJPEGImage method is:
234%
235% Image *ReadJPEGImage(const ImageInfo *image_info,
236% ExceptionInfo *exception)
237%
238% A description of each parameter follows:
239%
240% o image_info: the image info.
241%
242% o exception: return any errors or warnings in this structure.
243%
244*/
245
cristy3ed852e2009-09-05 21:47:34 +0000246static boolean FillInputBuffer(j_decompress_ptr cinfo)
247{
248 SourceManager
249 *source;
250
251 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000252 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
253 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000254 if (source->manager.bytes_in_buffer == 0)
255 {
cristy453b8202012-06-07 22:57:52 +0000256 if (source->start_of_blob != FALSE)
cristy3ed852e2009-09-05 21:47:34 +0000257 ERREXIT(cinfo,JERR_INPUT_EMPTY);
258 WARNMS(cinfo,JWRN_JPEG_EOF);
259 source->buffer[0]=(JOCTET) 0xff;
260 source->buffer[1]=(JOCTET) JPEG_EOI;
261 source->manager.bytes_in_buffer=2;
262 }
263 source->manager.next_input_byte=source->buffer;
264 source->start_of_blob=FALSE;
265 return(TRUE);
266}
267
268static int GetCharacter(j_decompress_ptr jpeg_info)
269{
270 if (jpeg_info->src->bytes_in_buffer == 0)
271 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
272 jpeg_info->src->bytes_in_buffer--;
273 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
274}
275
276static void InitializeSource(j_decompress_ptr cinfo)
277{
278 SourceManager
279 *source;
280
281 source=(SourceManager *) cinfo->src;
282 source->start_of_blob=TRUE;
283}
284
cristye8dd1302009-11-11 02:45:03 +0000285static MagickBooleanType IsITUFaxImage(const Image *image)
286{
287 const StringInfo
288 *profile;
289
290 const unsigned char
291 *datum;
292
cristyace6aa42009-11-11 03:17:33 +0000293 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000294 if (profile == (const StringInfo *) NULL)
295 return(MagickFalse);
296 if (GetStringInfoLength(profile) < 5)
297 return(MagickFalse);
298 datum=GetStringInfoDatum(profile);
299 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
300 (datum[3] == 0x41) && (datum[4] == 0x58))
301 return(MagickTrue);
302 return(MagickFalse);
303}
304
cristy437c3932011-10-12 18:03:46 +0000305static void JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000306{
cristyd28b1dd2011-05-14 20:30:38 +0000307 char
308 message[JMSG_LENGTH_MAX];
309
cristy3ed852e2009-09-05 21:47:34 +0000310 ErrorManager
311 *error_manager;
312
cristyc82a27b2011-10-21 01:07:16 +0000313 ExceptionInfo
314 *exception;
315
cristyd28b1dd2011-05-14 20:30:38 +0000316 Image
317 *image;
318
319 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000320 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000321 image=error_manager->image;
cristyc82a27b2011-10-21 01:07:16 +0000322 exception=error_manager->exception;
cristy86f33542011-05-21 22:58:33 +0000323 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000324 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
326 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000327 if (error_manager->finished != MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000328 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
329 (char *) message,"`%s'",image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000330 else
cristyc82a27b2011-10-21 01:07:16 +0000331 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
332 (char *) message,"`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000333 longjmp(error_manager->error_recovery,1);
334}
335
cristyd28b1dd2011-05-14 20:30:38 +0000336static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
337{
cristyf162e012012-03-28 12:54:33 +0000338#define JPEGExcessiveWarnings 1000
339
cristyd28b1dd2011-05-14 20:30:38 +0000340 char
341 message[JMSG_LENGTH_MAX];
342
343 ErrorManager
344 *error_manager;
345
cristy018f07f2011-09-04 21:15:19 +0000346 ExceptionInfo
347 *exception;
348
cristyd28b1dd2011-05-14 20:30:38 +0000349 Image
350 *image;
351
352 *message='\0';
353 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000354 exception=error_manager->exception;
cristyd28b1dd2011-05-14 20:30:38 +0000355 image=error_manager->image;
356 if (level < 0)
357 {
358 /*
359 Process warning message.
360 */
361 (jpeg_info->err->format_message)(jpeg_info,message);
cristyf162e012012-03-28 12:54:33 +0000362 if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
363 JPEGErrorHandler(jpeg_info);
cristy8043fb32012-06-28 16:14:12 +0000364 ThrowBinaryException(CorruptImageWarning,(char *) message,
365 image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000366 }
367 else
368 if ((image->debug != MagickFalse) &&
369 (level >= jpeg_info->err->trace_level))
370 {
371 /*
372 Process trace message.
373 */
374 (jpeg_info->err->format_message)(jpeg_info,message);
375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
376 "[%s] JPEG Trace: \"%s\"",image->filename,message);
377 }
378 return(MagickTrue);
379}
380
cristy3ed852e2009-09-05 21:47:34 +0000381static boolean ReadComment(j_decompress_ptr jpeg_info)
382{
cristy3ed852e2009-09-05 21:47:34 +0000383 ErrorManager
384 *error_manager;
385
cristy018f07f2011-09-04 21:15:19 +0000386 ExceptionInfo
387 *exception;
388
cristy3ed852e2009-09-05 21:47:34 +0000389 Image
390 *image;
391
cristyf551aee2012-09-27 21:45:23 +0000392 register unsigned char
cristy3ed852e2009-09-05 21:47:34 +0000393 *p;
394
cristybb503372010-05-27 20:51:26 +0000395 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000396 i;
397
398 size_t
399 length;
400
cristyf551aee2012-09-27 21:45:23 +0000401 StringInfo
402 *comment;
403
cristy3ed852e2009-09-05 21:47:34 +0000404 /*
405 Determine length of comment.
406 */
407 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000408 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000409 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000410 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000411 length+=GetCharacter(jpeg_info);
dirk896a34e2014-09-18 10:57:30 +0000412 if (length <= 2)
cristydf878012014-01-12 23:39:40 +0000413 return(TRUE);
dirk896a34e2014-09-18 10:57:30 +0000414 length-=2;
cristyf551aee2012-09-27 21:45:23 +0000415 comment=BlobToStringInfo((const void *) NULL,length);
416 if (comment == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000417 {
418 (void) ThrowMagickException(exception,GetMagickModule(),
419 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
420 return(FALSE);
421 }
cristy3ed852e2009-09-05 21:47:34 +0000422 /*
423 Read comment.
424 */
cristyf551aee2012-09-27 21:45:23 +0000425 error_manager->profile=comment;
426 p=GetStringInfoDatum(comment);
427 for (i=0; i < (ssize_t) GetStringInfoLength(comment); i++)
428 *p++=(unsigned char) GetCharacter(jpeg_info);
cristy3ed852e2009-09-05 21:47:34 +0000429 *p='\0';
cristyf551aee2012-09-27 21:45:23 +0000430 error_manager->profile=NULL;
431 p=GetStringInfoDatum(comment);
432 (void) SetImageProperty(image,"comment",(const char *) p,exception);
433 comment=DestroyStringInfo(comment);
cristydf878012014-01-12 23:39:40 +0000434 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000435}
436
437static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
438{
439 char
440 magick[12];
441
442 ErrorManager
443 *error_manager;
444
cristy018f07f2011-09-04 21:15:19 +0000445 ExceptionInfo
446 *exception;
447
cristy3ed852e2009-09-05 21:47:34 +0000448 Image
449 *image;
450
451 MagickBooleanType
452 status;
453
cristybb503372010-05-27 20:51:26 +0000454 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000455 i;
456
457 register unsigned char
458 *p;
459
460 size_t
461 length;
462
463 StringInfo
464 *icc_profile,
465 *profile;
466
467 /*
468 Read color profile.
469 */
cristybb503372010-05-27 20:51:26 +0000470 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000471 length+=(size_t) GetCharacter(jpeg_info);
472 length-=2;
473 if (length <= 14)
474 {
475 while (length-- > 0)
476 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000477 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000478 }
479 for (i=0; i < 12; i++)
480 magick[i]=(char) GetCharacter(jpeg_info);
481 if (LocaleCompare(magick,ICC_PROFILE) != 0)
482 {
483 /*
484 Not a ICC profile, return.
485 */
cristybb503372010-05-27 20:51:26 +0000486 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000487 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000488 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000489 }
490 (void) GetCharacter(jpeg_info); /* id */
491 (void) GetCharacter(jpeg_info); /* markers */
492 length-=14;
493 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000494 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000495 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000496 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000497 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000498 {
499 (void) ThrowMagickException(exception,GetMagickModule(),
500 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
501 return(FALSE);
502 }
cristyf551aee2012-09-27 21:45:23 +0000503 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000504 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000505 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000506 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000507 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000508 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
509 if (icc_profile != (StringInfo *) NULL)
510 {
511 ConcatenateStringInfo(icc_profile,profile);
512 profile=DestroyStringInfo(profile);
513 }
514 else
515 {
cristyd15e6592011-10-15 00:13:06 +0000516 status=SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000517 profile=DestroyStringInfo(profile);
518 if (status == MagickFalse)
cristy9bb7c842014-06-17 23:42:24 +0000519 {
520 (void) ThrowMagickException(exception,GetMagickModule(),
521 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
522 return(FALSE);
523 }
cristy3ed852e2009-09-05 21:47:34 +0000524 }
525 if (image->debug != MagickFalse)
526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000527 "Profile: ICC, %.20g bytes",(double) length);
cristydf878012014-01-12 23:39:40 +0000528 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000529}
530
531static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
532{
533 char
cristy151b66d2015-04-15 10:50:31 +0000534 magick[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000535
536 ErrorManager
537 *error_manager;
538
cristy018f07f2011-09-04 21:15:19 +0000539 ExceptionInfo
540 *exception;
541
cristy3ed852e2009-09-05 21:47:34 +0000542 Image
543 *image;
544
545 MagickBooleanType
546 status;
547
cristybb503372010-05-27 20:51:26 +0000548 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000549 i;
550
551 register unsigned char
552 *p;
553
554 size_t
555 length;
556
557 StringInfo
558 *iptc_profile,
559 *profile;
560
561 /*
562 Determine length of binary data stored here.
563 */
cristybb503372010-05-27 20:51:26 +0000564 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000565 length+=(size_t) GetCharacter(jpeg_info);
566 length-=2;
567 if (length <= 14)
568 {
569 while (length-- > 0)
570 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000571 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000572 }
573 /*
574 Validate that this was written as a Photoshop resource format slug.
575 */
576 for (i=0; i < 10; i++)
577 magick[i]=(char) GetCharacter(jpeg_info);
578 magick[10]='\0';
cristy4d0ca342014-05-01 00:42:09 +0000579 length-=10;
cristy3ed852e2009-09-05 21:47:34 +0000580 if (length <= 10)
cristydf878012014-01-12 23:39:40 +0000581 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000582 if (LocaleCompare(magick,"Photoshop ") != 0)
583 {
584 /*
585 Not a IPTC profile, return.
586 */
cristybb503372010-05-27 20:51:26 +0000587 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000588 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000589 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000590 }
591 /*
592 Remove the version number.
593 */
594 for (i=0; i < 4; i++)
595 (void) GetCharacter(jpeg_info);
cristy35892192014-05-26 12:04:36 +0000596 if (length <= 11)
cristy58a749e2014-05-25 17:36:53 +0000597 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000598 length-=4;
cristy3ed852e2009-09-05 21:47:34 +0000599 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000600 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000601 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000602 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000603 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000604 {
605 (void) ThrowMagickException(exception,GetMagickModule(),
606 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
607 return(FALSE);
608 }
cristyf551aee2012-09-27 21:45:23 +0000609 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000610 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000611 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000612 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000613 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000614 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
615 if (iptc_profile != (StringInfo *) NULL)
616 {
617 ConcatenateStringInfo(iptc_profile,profile);
618 profile=DestroyStringInfo(profile);
619 }
620 else
621 {
cristyd15e6592011-10-15 00:13:06 +0000622 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000623 profile=DestroyStringInfo(profile);
624 if (status == MagickFalse)
cristy9bb7c842014-06-17 23:42:24 +0000625 {
626 (void) ThrowMagickException(exception,GetMagickModule(),
627 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
628 return(FALSE);
629 }
cristy3ed852e2009-09-05 21:47:34 +0000630 }
631 if (image->debug != MagickFalse)
632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000633 "Profile: iptc, %.20g bytes",(double) length);
cristydf878012014-01-12 23:39:40 +0000634 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000635}
636
637static boolean ReadProfile(j_decompress_ptr jpeg_info)
638{
639 char
cristy151b66d2015-04-15 10:50:31 +0000640 name[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000641
cristye23ec9d2011-08-16 18:15:40 +0000642 const StringInfo
643 *previous_profile;
644
cristy3ed852e2009-09-05 21:47:34 +0000645 ErrorManager
646 *error_manager;
647
cristy018f07f2011-09-04 21:15:19 +0000648 ExceptionInfo
649 *exception;
650
cristy3ed852e2009-09-05 21:47:34 +0000651 Image
652 *image;
653
654 int
655 marker;
656
657 MagickBooleanType
658 status;
659
cristybb503372010-05-27 20:51:26 +0000660 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000661 i;
662
663 register unsigned char
664 *p;
665
666 size_t
667 length;
668
669 StringInfo
670 *profile;
671
672 /*
673 Read generic profile.
674 */
cristybb503372010-05-27 20:51:26 +0000675 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000676 length+=(size_t) GetCharacter(jpeg_info);
677 if (length <= 2)
cristydf878012014-01-12 23:39:40 +0000678 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000679 length-=2;
680 marker=jpeg_info->unread_marker-JPEG_APP0;
cristy151b66d2015-04-15 10:50:31 +0000681 (void) FormatLocaleString(name,MagickPathExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000682 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000683 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000684 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000685 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000686 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000687 {
688 (void) ThrowMagickException(exception,GetMagickModule(),
689 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
690 return(FALSE);
691 }
cristyf551aee2012-09-27 21:45:23 +0000692 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000693 p=GetStringInfoDatum(profile);
cristy08f255a2011-08-17 01:23:06 +0000694 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000695 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000696 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000697 if (marker == 1)
698 {
699 p=GetStringInfoDatum(profile);
700 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
cristy151b66d2015-04-15 10:50:31 +0000701 (void) CopyMagickString(name,"exif",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000702 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
703 {
cristybb503372010-05-27 20:51:26 +0000704 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000705 j;
706
707 /*
708 Extract namespace from XMP profile.
709 */
710 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000711 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000712 {
713 if (*p == '\0')
714 break;
715 p++;
716 }
cristybb503372010-05-27 20:51:26 +0000717 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000718 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
cristy151b66d2015-04-15 10:50:31 +0000719 (void) CopyMagickString(name,"xmp",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000720 }
721 }
cristye23ec9d2011-08-16 18:15:40 +0000722 previous_profile=GetImageProfile(image,name);
723 if (previous_profile != (const StringInfo *) NULL)
cristy08f255a2011-08-17 01:23:06 +0000724 {
cristy1b58f252012-03-01 01:41:41 +0000725 size_t
dirkc93932a2015-10-01 16:56:10 +0200726 profile_length;
cristy05c0c9a2011-09-05 23:16:13 +0000727
dirkc93932a2015-10-01 16:56:10 +0200728 profile_length=GetStringInfoLength(profile);
cristy08f255a2011-08-17 01:23:06 +0000729 SetStringInfoLength(profile,GetStringInfoLength(profile)+
730 GetStringInfoLength(previous_profile));
cristy83ab3d82011-09-06 12:05:51 +0000731 (void) memmove(GetStringInfoDatum(profile)+
732 GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
dirkc93932a2015-10-01 16:56:10 +0200733 profile_length);
cristy08f255a2011-08-17 01:23:06 +0000734 (void) memcpy(GetStringInfoDatum(profile),
735 GetStringInfoDatum(previous_profile),
736 GetStringInfoLength(previous_profile));
737 }
cristyd15e6592011-10-15 00:13:06 +0000738 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000739 profile=DestroyStringInfo(profile);
740 if (status == MagickFalse)
cristydf878012014-01-12 23:39:40 +0000741 {
742 (void) ThrowMagickException(exception,GetMagickModule(),
743 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
744 return(FALSE);
745 }
cristy3ed852e2009-09-05 21:47:34 +0000746 if (image->debug != MagickFalse)
747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000748 "Profile: %s, %.20g bytes",name,(double) length);
cristydf878012014-01-12 23:39:40 +0000749 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000750}
751
cristyf2faecf2010-05-28 19:19:36 +0000752static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000753{
754 SourceManager
755 *source;
756
757 if (number_bytes <= 0)
758 return;
759 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000760 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000761 {
cristyf2faecf2010-05-28 19:19:36 +0000762 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000763 (void) FillInputBuffer(cinfo);
764 }
cristy4cb162a2010-05-30 03:04:47 +0000765 source->manager.next_input_byte+=number_bytes;
766 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000767}
768
769static void TerminateSource(j_decompress_ptr cinfo)
770{
cristydf0d90e2011-12-12 01:03:55 +0000771 (void) cinfo;
cristy3ed852e2009-09-05 21:47:34 +0000772}
773
774static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
775{
776 SourceManager
777 *source;
778
779 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
780 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
781 source=(SourceManager *) cinfo->src;
782 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
783 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
784 source=(SourceManager *) cinfo->src;
785 source->manager.init_source=InitializeSource;
786 source->manager.fill_input_buffer=FillInputBuffer;
787 source->manager.skip_input_data=SkipInputData;
788 source->manager.resync_to_restart=jpeg_resync_to_restart;
789 source->manager.term_source=TerminateSource;
790 source->manager.bytes_in_buffer=0;
791 source->manager.next_input_byte=NULL;
792 source->image=image;
793}
794
795static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
dirk881355a2013-08-19 19:56:50 +0000796 Image *image, ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000797{
798 image->quality=UndefinedCompressionQuality;
799#if defined(D_PROGRESSIVE_SUPPORTED)
800 if (image->compression == LosslessJPEGCompression)
801 {
dirk29dd80e2013-10-31 23:11:11 +0000802 image->quality=100;
cristy3ed852e2009-09-05 21:47:34 +0000803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
804 "Quality: 100 (lossless)");
805 }
806 else
807#endif
808 {
cristybb503372010-05-27 20:51:26 +0000809 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000810 j,
811 qvalue,
812 sum;
813
cristybb503372010-05-27 20:51:26 +0000814 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000815 i;
816
817 /*
818 Determine the JPEG compression quality from the quantization tables.
819 */
820 sum=0;
821 for (i=0; i < NUM_QUANT_TBLS; i++)
822 {
823 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
824 for (j=0; j < DCTSIZE2; j++)
825 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
dirk881355a2013-08-19 19:56:50 +0000826 }
827 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
828 (jpeg_info->quant_tbl_ptrs[1] != NULL))
829 {
830 ssize_t
831 hash[101] =
832 {
833 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
834 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
835 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
836 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
837 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
838 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
839 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
840 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
841 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
842 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
843 0
844 },
845 sums[101] =
846 {
847 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
848 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
849 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
850 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
851 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
852 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
853 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
854 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
855 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
856 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
857 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
858 128, 0
859 };
860
861 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
862 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
863 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
864 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
865 for (i=0; i < 100; i++)
866 {
867 if ((qvalue < hash[i]) && (sum < sums[i]))
868 continue;
869 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000870 image->quality=(size_t) i+1;
dirk881355a2013-08-19 19:56:50 +0000871 if (image->debug != MagickFalse)
872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
873 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
874 (sum <= sums[i]) ? "exact" : "approximate");
875 break;
876 }
877 }
878 else
879 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
880 {
881 ssize_t
882 hash[101] =
883 {
884 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
885 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
886 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
887 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
888 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
889 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
890 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
891 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
892 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
893 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
cristy3ed852e2009-09-05 21:47:34 +0000894 0
dirk881355a2013-08-19 19:56:50 +0000895 },
896 sums[101] =
897 {
898 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
899 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
900 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
901 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
902 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
903 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
904 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
905 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
906 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
907 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
908 667, 592, 518, 441, 369, 292, 221, 151, 86,
909 64, 0
910 };
cristy3ed852e2009-09-05 21:47:34 +0000911
dirk881355a2013-08-19 19:56:50 +0000912 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
913 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
914 for (i=0; i < 100; i++)
915 {
916 if ((qvalue < hash[i]) && (sum < sums[i]))
917 continue;
918 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000919 image->quality=(size_t)i+1;
dirk881355a2013-08-19 19:56:50 +0000920 if (image->debug != MagickFalse)
921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
922 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
923 (sum <= sums[i]) ? "exact" : "approximate");
924 break;
925 }
926 }
cristy3ed852e2009-09-05 21:47:34 +0000927 }
928}
929
cristyd15e6592011-10-15 00:13:06 +0000930static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000931{
932 char
cristy151b66d2015-04-15 10:50:31 +0000933 sampling_factor[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000934
935 switch (jpeg_info->out_color_space)
936 {
937 case JCS_CMYK:
938 {
939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristy151b66d2015-04-15 10:50:31 +0000940 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000941 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
942 jpeg_info->comp_info[0].v_samp_factor,
943 jpeg_info->comp_info[1].h_samp_factor,
944 jpeg_info->comp_info[1].v_samp_factor,
945 jpeg_info->comp_info[2].h_samp_factor,
946 jpeg_info->comp_info[2].v_samp_factor,
947 jpeg_info->comp_info[3].h_samp_factor,
948 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000949 break;
cristy3ed852e2009-09-05 21:47:34 +0000950 }
951 case JCS_GRAYSCALE:
952 {
953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
954 "Colorspace: GRAYSCALE");
cristy151b66d2015-04-15 10:50:31 +0000955 (void) FormatLocaleString(sampling_factor,MagickPathExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000956 jpeg_info->comp_info[0].h_samp_factor,
957 jpeg_info->comp_info[0].v_samp_factor);
958 break;
959 }
960 case JCS_RGB:
961 {
962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristy151b66d2015-04-15 10:50:31 +0000963 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000964 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
965 jpeg_info->comp_info[0].v_samp_factor,
966 jpeg_info->comp_info[1].h_samp_factor,
967 jpeg_info->comp_info[1].v_samp_factor,
968 jpeg_info->comp_info[2].h_samp_factor,
969 jpeg_info->comp_info[2].v_samp_factor);
970 break;
971 }
972 default:
973 {
974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
975 jpeg_info->out_color_space);
cristy151b66d2015-04-15 10:50:31 +0000976 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000977 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
978 jpeg_info->comp_info[0].v_samp_factor,
979 jpeg_info->comp_info[1].h_samp_factor,
980 jpeg_info->comp_info[1].v_samp_factor,
981 jpeg_info->comp_info[2].h_samp_factor,
982 jpeg_info->comp_info[2].v_samp_factor,
983 jpeg_info->comp_info[3].h_samp_factor,
984 jpeg_info->comp_info[3].v_samp_factor);
985 break;
986 }
987 }
cristyd15e6592011-10-15 00:13:06 +0000988 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
989 exception);
cristye90d7402010-03-14 18:21:29 +0000990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
991 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000992}
993
994static Image *ReadJPEGImage(const ImageInfo *image_info,
995 ExceptionInfo *exception)
996{
997 char
cristy151b66d2015-04-15 10:50:31 +0000998 value[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000999
cristycaec74e2009-09-14 02:20:04 +00001000 const char
cristy11151212009-09-14 13:10:15 +00001001 *option;
cristycaec74e2009-09-14 02:20:04 +00001002
cristy3ed852e2009-09-05 21:47:34 +00001003 ErrorManager
1004 error_manager;
1005
cristy3ed852e2009-09-05 21:47:34 +00001006 Image
1007 *image;
1008
cristy3ed852e2009-09-05 21:47:34 +00001009 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00001010 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001011
1012 JSAMPROW
1013 scanline[1];
1014
1015 MagickBooleanType
1016 debug,
1017 status;
1018
1019 MagickSizeType
1020 number_pixels;
1021
cristy22646e22013-06-23 16:34:03 +00001022 MemoryInfo
1023 *memory_info;
1024
cristy4c08aed2011-07-01 19:47:50 +00001025 Quantum
1026 index;
1027
cristybb503372010-05-27 20:51:26 +00001028 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001029 i;
1030
1031 struct jpeg_decompress_struct
1032 jpeg_info;
1033
1034 struct jpeg_error_mgr
1035 jpeg_error;
1036
1037 register JSAMPLE
1038 *p;
1039
cristybb503372010-05-27 20:51:26 +00001040 size_t
cristy3ed852e2009-09-05 21:47:34 +00001041 units;
1042
cristy524222d2011-04-25 00:37:06 +00001043 ssize_t
1044 y;
1045
cristy3ed852e2009-09-05 21:47:34 +00001046 /*
1047 Open image file.
1048 */
1049 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001050 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001051 if (image_info->debug != MagickFalse)
1052 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1053 image_info->filename);
1054 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001055 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001056 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +00001057 (void) debug;
cristy9950d572011-10-01 18:22:35 +00001058 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001059 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1060 if (status == MagickFalse)
1061 {
1062 image=DestroyImageList(image);
1063 return((Image *) NULL);
1064 }
1065 /*
1066 Initialize JPEG parameters.
1067 */
cristy91044972011-04-22 14:21:16 +00001068 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001069 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1070 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1071 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001072 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +00001073 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy72f87f82013-06-23 16:43:05 +00001074 memory_info=(MemoryInfo *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001075 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001076 error_manager.image=image;
1077 if (setjmp(error_manager.error_recovery) != 0)
1078 {
1079 jpeg_destroy_decompress(&jpeg_info);
cristyf551aee2012-09-27 21:45:23 +00001080 if (error_manager.profile != (StringInfo *) NULL)
1081 error_manager.profile=DestroyStringInfo(error_manager.profile);
cristy3ed852e2009-09-05 21:47:34 +00001082 (void) CloseBlob(image);
1083 number_pixels=(MagickSizeType) image->columns*image->rows;
1084 if (number_pixels != 0)
1085 return(GetFirstImageInList(image));
1086 return(DestroyImage(image));
1087 }
1088 jpeg_info.client_data=(void *) &error_manager;
1089 jpeg_create_decompress(&jpeg_info);
1090 JPEGSourceManager(&jpeg_info,image);
1091 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
cristy00db0212014-06-25 23:01:19 +00001092 option=GetImageOption(image_info,"profile:skip");
cristy53b6cc32014-06-25 13:13:33 +00001093 if (IsOptionMember("ICC",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001094 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
cristy53b6cc32014-06-25 13:13:33 +00001095 if (IsOptionMember("IPTC",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001096 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
cristy3ed852e2009-09-05 21:47:34 +00001097 for (i=1; i < 16; i++)
1098 if ((i != 2) && (i != 13) && (i != 14))
cristy53b6cc32014-06-25 13:13:33 +00001099 if (IsOptionMember("APP",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001100 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristydf878012014-01-12 23:39:40 +00001101 i=(ssize_t) jpeg_read_header(&jpeg_info,TRUE);
cristy53215c82009-09-19 16:32:36 +00001102 if ((image_info->colorspace == YCbCrColorspace) ||
1103 (image_info->colorspace == Rec601YCbCrColorspace) ||
1104 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001105 jpeg_info.out_color_space=JCS_YCbCr;
1106 /*
1107 Set image resolution.
1108 */
1109 units=0;
1110 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1111 (jpeg_info.Y_density != 1))
1112 {
cristy2a11bef2011-10-28 18:33:11 +00001113 image->resolution.x=(double) jpeg_info.X_density;
1114 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001115 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001116 }
1117 if (units == 1)
1118 image->units=PixelsPerInchResolution;
1119 if (units == 2)
1120 image->units=PixelsPerCentimeterResolution;
1121 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy092ec8d2013-04-26 13:46:22 +00001122 option=GetImageOption(image_info,"jpeg:size");
cristy7e7d8e92014-10-25 22:24:51 +00001123 if ((option != (const char *) NULL) &&
1124 (jpeg_info.out_color_space != JCS_YCbCr))
cristy3ed852e2009-09-05 21:47:34 +00001125 {
1126 double
1127 scale_factor;
1128
cristycaec74e2009-09-14 02:20:04 +00001129 GeometryInfo
1130 geometry_info;
1131
cristy0adb4f92009-11-28 18:08:51 +00001132 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001133 flags;
1134
cristy3ed852e2009-09-05 21:47:34 +00001135 /*
cristycaec74e2009-09-14 02:20:04 +00001136 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001137 */
cristy11151212009-09-14 13:10:15 +00001138 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001139 if ((flags & SigmaValue) == 0)
1140 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001141 jpeg_calc_output_dimensions(&jpeg_info);
1142 image->magick_columns=jpeg_info.output_width;
1143 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001144 scale_factor=1.0;
1145 if (geometry_info.rho != 0.0)
1146 scale_factor=jpeg_info.output_width/geometry_info.rho;
1147 if ((geometry_info.sigma != 0.0) &&
1148 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1149 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001150 jpeg_info.scale_num=1U;
1151 jpeg_info.scale_denom=(unsigned int) scale_factor;
1152 jpeg_calc_output_dimensions(&jpeg_info);
1153 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1155 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001156 }
cristy3ed852e2009-09-05 21:47:34 +00001157#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1158#if defined(D_LOSSLESS_SUPPORTED)
1159 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1160 JPEGInterlace : NoInterlace;
1161 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1162 LosslessJPEGCompression : JPEGCompression;
1163 if (jpeg_info.data_precision > 8)
1164 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1165 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1166 image->filename);
1167 if (jpeg_info.data_precision == 16)
1168 jpeg_info.data_precision=12;
1169#else
1170 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1171 NoInterlace;
1172 image->compression=JPEGCompression;
1173#endif
1174#else
1175 image->compression=JPEGCompression;
1176 image->interlace=JPEGInterlace;
1177#endif
cristy092ec8d2013-04-26 13:46:22 +00001178 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001179 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001180 {
cristyb4bb39c2012-02-21 18:45:54 +00001181 /*
1182 Let the JPEG library quantize the image.
1183 */
cristydf878012014-01-12 23:39:40 +00001184 jpeg_info.quantize_colors=TRUE;
anthony41906552011-10-07 12:15:27 +00001185 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001186 }
cristy092ec8d2013-04-26 13:46:22 +00001187 option=GetImageOption(image_info,"jpeg:block-smoothing");
cristy0d737182013-04-04 00:22:57 +00001188 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001189 jpeg_info.do_block_smoothing=IsStringTrue(option) != MagickFalse ? TRUE :
1190 FALSE;
cristyb4bb39c2012-02-21 18:45:54 +00001191 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00001192 option=GetImageOption(image_info,"jpeg:dct-method");
cristy787d4352010-03-06 13:55:58 +00001193 if (option != (const char *) NULL)
1194 switch (*option)
1195 {
1196 case 'D':
1197 case 'd':
1198 {
1199 if (LocaleCompare(option,"default") == 0)
1200 jpeg_info.dct_method=JDCT_DEFAULT;
1201 break;
1202 }
1203 case 'F':
1204 case 'f':
1205 {
1206 if (LocaleCompare(option,"fastest") == 0)
1207 jpeg_info.dct_method=JDCT_FASTEST;
1208 if (LocaleCompare(option,"float") == 0)
1209 jpeg_info.dct_method=JDCT_FLOAT;
1210 break;
1211 }
1212 case 'I':
1213 case 'i':
1214 {
1215 if (LocaleCompare(option,"ifast") == 0)
1216 jpeg_info.dct_method=JDCT_IFAST;
1217 if (LocaleCompare(option,"islow") == 0)
1218 jpeg_info.dct_method=JDCT_ISLOW;
1219 break;
1220 }
1221 }
cristy092ec8d2013-04-26 13:46:22 +00001222 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
cristy0d737182013-04-04 00:22:57 +00001223 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001224 jpeg_info.do_fancy_upsampling=IsStringTrue(option) != MagickFalse ? TRUE :
1225 FALSE;
cristy3ed852e2009-09-05 21:47:34 +00001226 (void) jpeg_start_decompress(&jpeg_info);
1227 image->columns=jpeg_info.output_width;
1228 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001229 image->depth=(size_t) jpeg_info.data_precision;
cristy93ff2cc2012-05-13 21:03:53 +00001230 switch (jpeg_info.out_color_space)
1231 {
1232 case JCS_RGB:
1233 default:
1234 {
cristyf551aee2012-09-27 21:45:23 +00001235 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001236 break;
1237 }
1238 case JCS_GRAYSCALE:
1239 {
cristy1a45be72013-04-07 00:16:24 +00001240 (void) SetImageColorspace(image,GRAYColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001241 break;
1242 }
1243 case JCS_YCbCr:
1244 {
cristyf551aee2012-09-27 21:45:23 +00001245 (void) SetImageColorspace(image,YCbCrColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001246 break;
1247 }
1248 case JCS_CMYK:
1249 {
cristyf551aee2012-09-27 21:45:23 +00001250 (void) SetImageColorspace(image,CMYKColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001251 break;
1252 }
1253 }
cristy36fc5502012-05-22 11:32:23 +00001254 if (IsITUFaxImage(image) != MagickFalse)
1255 {
cristyf551aee2012-09-27 21:45:23 +00001256 (void) SetImageColorspace(image,LabColorspace,exception);
cristy36fc5502012-05-22 11:32:23 +00001257 jpeg_info.out_color_space=JCS_YCbCr;
1258 }
cristy092ec8d2013-04-26 13:46:22 +00001259 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001260 if (option != (const char *) NULL)
1261 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1262 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001263 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1264 if ((jpeg_info.output_components == 1) &&
1265 (jpeg_info.quantize_colors == MagickFalse))
1266 {
cristybb503372010-05-27 20:51:26 +00001267 size_t
cristy3ed852e2009-09-05 21:47:34 +00001268 colors;
1269
cristybb503372010-05-27 20:51:26 +00001270 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001271 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001272 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1273 }
1274 if (image->debug != MagickFalse)
1275 {
1276 if (image->interlace != NoInterlace)
1277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1278 "Interlace: progressive");
1279 else
1280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1281 "Interlace: nonprogressive");
1282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1283 (int) jpeg_info.data_precision);
1284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1285 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1286 }
dirk881355a2013-08-19 19:56:50 +00001287 JPEGSetImageQuality(&jpeg_info,image,exception);
cristyd15e6592011-10-15 00:13:06 +00001288 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristy151b66d2015-04-15 10:50:31 +00001289 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001290 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001291 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001292 if (image_info->ping != MagickFalse)
1293 {
1294 jpeg_destroy_decompress(&jpeg_info);
1295 (void) CloseBlob(image);
1296 return(GetFirstImageInList(image));
1297 }
cristyacabb842014-12-14 23:36:33 +00001298 status=SetImageExtent(image,image->columns,image->rows,exception);
1299 if (status == MagickFalse)
cristy457f81d2015-02-01 18:13:23 +00001300 {
1301 jpeg_destroy_decompress(&jpeg_info);
1302 return(DestroyImageList(image));
1303 }
1304 if ((jpeg_info.output_components != 1) &&
1305 (jpeg_info.output_components != 3) && (jpeg_info.output_components != 4))
1306 {
1307 jpeg_destroy_decompress(&jpeg_info);
1308 ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
1309 }
cristy22646e22013-06-23 16:34:03 +00001310 memory_info=AcquireVirtualMemory((size_t) image->columns,
1311 jpeg_info.output_components*sizeof(*jpeg_pixels));
1312 if (memory_info == (MemoryInfo *) NULL)
cristyee1bdaa2013-07-16 16:45:03 +00001313 {
1314 jpeg_destroy_decompress(&jpeg_info);
1315 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1316 }
cristy22646e22013-06-23 16:34:03 +00001317 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001318 /*
1319 Convert JPEG pixels to pixel packets.
1320 */
1321 if (setjmp(error_manager.error_recovery) != 0)
1322 {
cristy22646e22013-06-23 16:34:03 +00001323 if (memory_info != (MemoryInfo *) NULL)
1324 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001325 jpeg_destroy_decompress(&jpeg_info);
1326 (void) CloseBlob(image);
1327 number_pixels=(MagickSizeType) image->columns*image->rows;
1328 if (number_pixels != 0)
1329 return(GetFirstImageInList(image));
1330 return(DestroyImage(image));
1331 }
1332 if (jpeg_info.quantize_colors != MagickFalse)
1333 {
cristybb503372010-05-27 20:51:26 +00001334 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001335 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001336 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001337 {
cristy1b58f252012-03-01 01:41:41 +00001338 image->colormap[i].red=(double) ScaleCharToQuantum(
1339 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001340 image->colormap[i].green=image->colormap[i].red;
1341 image->colormap[i].blue=image->colormap[i].red;
Cristy71406342015-09-11 19:49:21 -04001342 image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001343 }
1344 else
cristybb503372010-05-27 20:51:26 +00001345 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001346 {
cristy1b58f252012-03-01 01:41:41 +00001347 image->colormap[i].red=(double) ScaleCharToQuantum(
1348 jpeg_info.colormap[0][i]);
1349 image->colormap[i].green=(double) ScaleCharToQuantum(
1350 jpeg_info.colormap[1][i]);
1351 image->colormap[i].blue=(double) ScaleCharToQuantum(
1352 jpeg_info.colormap[2][i]);
Cristy71406342015-09-11 19:49:21 -04001353 image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001354 }
1355 }
1356 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001357 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001358 {
cristybb503372010-05-27 20:51:26 +00001359 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001360 x;
1361
cristy4c08aed2011-07-01 19:47:50 +00001362 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001363 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001364
1365 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1366 {
1367 (void) ThrowMagickException(exception,GetMagickModule(),
1368 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1369 continue;
1370 }
1371 p=jpeg_pixels;
1372 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001373 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001374 break;
cristy3ed852e2009-09-05 21:47:34 +00001375 if (jpeg_info.data_precision > 8)
1376 {
cristy5328a4c2013-12-03 11:32:13 +00001377 unsigned short
1378 scale;
1379
Cristy71406342015-09-11 19:49:21 -04001380 scale=65535/(unsigned short) GetQuantumRange((size_t)
1381 jpeg_info.data_precision);
cristy3ed852e2009-09-05 21:47:34 +00001382 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001383 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001384 {
Cristy71406342015-09-11 19:49:21 -04001385 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001386 pixel;
1387
Cristy71406342015-09-11 19:49:21 -04001388 pixel=(ssize_t) (scale*GETJSAMPLE(*p));
1389 index=(Quantum) ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001390 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001391 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001392 p++;
cristyed231572011-07-14 02:18:59 +00001393 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001394 }
1395 else
1396 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001397 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001398 {
Cristy71406342015-09-11 19:49:21 -04001399 SetPixelRed(image,ScaleShortToQuantum(
1400 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1401 SetPixelGreen(image,ScaleShortToQuantum(
1402 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1403 SetPixelBlue(image,ScaleShortToQuantum(
1404 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
cristy4c08aed2011-07-01 19:47:50 +00001405 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001406 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001407 }
1408 else
cristybb503372010-05-27 20:51:26 +00001409 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001410 {
Cristy71406342015-09-11 19:49:21 -04001411 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1412 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1413 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1414 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1415 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1416 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1417 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1418 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
cristy4c08aed2011-07-01 19:47:50 +00001419 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001420 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001421 }
1422 }
1423 else
1424 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001425 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001426 {
Cristy71406342015-09-11 19:49:21 -04001427 index=(Quantum) ConstrainColormapIndex(image,(ssize_t) GETJSAMPLE(*p),
1428 exception);
cristy4c08aed2011-07-01 19:47:50 +00001429 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001430 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001431 p++;
cristyed231572011-07-14 02:18:59 +00001432 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001433 }
1434 else
1435 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001436 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001437 {
cristy4c08aed2011-07-01 19:47:50 +00001438 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1439 GETJSAMPLE(*p++)),q);
1440 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1441 GETJSAMPLE(*p++)),q);
1442 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1443 GETJSAMPLE(*p++)),q);
1444 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001445 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001446 }
1447 else
cristybb503372010-05-27 20:51:26 +00001448 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001449 {
cristy4c08aed2011-07-01 19:47:50 +00001450 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1451 (unsigned char) GETJSAMPLE(*p++)),q);
1452 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1453 (unsigned char) GETJSAMPLE(*p++)),q);
1454 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1455 (unsigned char) GETJSAMPLE(*p++)),q);
1456 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1457 (unsigned char) GETJSAMPLE(*p++)),q);
1458 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001459 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001460 }
1461 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1462 break;
cristy524222d2011-04-25 00:37:06 +00001463 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1464 image->rows);
1465 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001466 {
1467 jpeg_abort_decompress(&jpeg_info);
1468 break;
1469 }
cristy3ed852e2009-09-05 21:47:34 +00001470 }
cristyd28b1dd2011-05-14 20:30:38 +00001471 if (status != MagickFalse)
1472 {
1473 error_manager.finished=MagickTrue;
1474 if (setjmp(error_manager.error_recovery) == 0)
1475 (void) jpeg_finish_decompress(&jpeg_info);
1476 }
cristy3ed852e2009-09-05 21:47:34 +00001477 /*
1478 Free jpeg resources.
1479 */
cristy3ed852e2009-09-05 21:47:34 +00001480 jpeg_destroy_decompress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00001481 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001482 (void) CloseBlob(image);
1483 return(GetFirstImageInList(image));
1484}
1485#endif
cristy86b6e552015-06-06 13:25:16 +00001486
cristy3ed852e2009-09-05 21:47:34 +00001487/*
1488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1489% %
1490% %
1491% %
1492% R e g i s t e r J P E G I m a g e %
1493% %
1494% %
1495% %
1496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1497%
1498% RegisterJPEGImage() adds properties for the JPEG image format to
1499% the list of supported formats. The properties include the image format
1500% tag, a method to read and/or write the format, whether the format
1501% supports the saving of more than one frame to the same file or blob,
1502% whether the format supports native in-memory I/O, and a brief
1503% description of the format.
1504%
1505% The format of the RegisterJPEGImage method is:
1506%
cristybb503372010-05-27 20:51:26 +00001507% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001508%
1509*/
cristybb503372010-05-27 20:51:26 +00001510ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001511{
dirk06b627a2015-04-06 18:59:17 +00001512#define JPEGDescription "Joint Photographic Experts Group JFIF format"
1513
cristy3ed852e2009-09-05 21:47:34 +00001514 char
cristy151b66d2015-04-15 10:50:31 +00001515 version[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001516
1517 MagickInfo
1518 *entry;
1519
cristy3ed852e2009-09-05 21:47:34 +00001520 *version='\0';
1521#if defined(JPEG_LIB_VERSION)
cristy151b66d2015-04-15 10:50:31 +00001522 (void) FormatLocaleString(version,MagickPathExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001523#endif
dirk06b627a2015-04-06 18:59:17 +00001524 entry=AcquireMagickInfo("JPEG","JPE",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001525#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001526 entry->flags^=CoderDecoderThreadSupportFlag;
cristyb338b712014-11-27 14:06:34 +00001527#endif
1528#if defined(MAGICKCORE_JPEG_DELEGATE)
1529 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1530 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1531#endif
1532 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001533 entry->flags^=CoderAdjoinFlag;
1534 entry->flags^=CoderUseExtensionFlag;
cristyb338b712014-11-27 14:06:34 +00001535 if (*version != '\0')
1536 entry->version=ConstantString(version);
1537 entry->mime_type=ConstantString("image/jpeg");
cristyb338b712014-11-27 14:06:34 +00001538 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001539 entry=AcquireMagickInfo("JPEG","JPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001540#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001541 entry->flags^=CoderDecoderThreadSupportFlag;
cristy61c382d2014-09-17 11:18:11 +00001542#endif
cristy3ed852e2009-09-05 21:47:34 +00001543#if defined(MAGICKCORE_JPEG_DELEGATE)
1544 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1545 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1546#endif
1547 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001548 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +00001549 if (*version != '\0')
1550 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001551 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001552 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001553 entry=AcquireMagickInfo("JPEG","JPG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001554#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001555 entry->flags^=CoderDecoderThreadSupportFlag;
cristy61c382d2014-09-17 11:18:11 +00001556#endif
cristy3ed852e2009-09-05 21:47:34 +00001557#if defined(MAGICKCORE_JPEG_DELEGATE)
1558 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1559 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1560#endif
dirk08e9a112015-02-22 01:51:41 +00001561 entry->flags^=CoderAdjoinFlag;
1562 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001563 if (*version != '\0')
1564 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001565 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001566 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001567 entry=AcquireMagickInfo("JPEG","JPS",JPEGDescription);
cristya663a4f2015-01-23 12:11:57 +00001568#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001569 entry->flags^=CoderDecoderThreadSupportFlag;
cristya663a4f2015-01-23 12:11:57 +00001570#endif
1571#if defined(MAGICKCORE_JPEG_DELEGATE)
1572 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1573 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1574#endif
dirk08e9a112015-02-22 01:51:41 +00001575 entry->flags^=CoderAdjoinFlag;
1576 entry->flags^=CoderUseExtensionFlag;
cristya663a4f2015-01-23 12:11:57 +00001577 if (*version != '\0')
1578 entry->version=ConstantString(version);
1579 entry->mime_type=ConstantString("image/jpeg");
cristya663a4f2015-01-23 12:11:57 +00001580 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001581 entry=AcquireMagickInfo("JPEG","PJPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001582#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001583 entry->flags^=CoderDecoderThreadSupportFlag;
cristy61c382d2014-09-17 11:18:11 +00001584#endif
cristy3ed852e2009-09-05 21:47:34 +00001585#if defined(MAGICKCORE_JPEG_DELEGATE)
1586 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1587 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1588#endif
dirk08e9a112015-02-22 01:51:41 +00001589 entry->flags^=CoderAdjoinFlag;
1590 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001591 if (*version != '\0')
1592 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001593 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001594 (void) RegisterMagickInfo(entry);
1595 return(MagickImageCoderSignature);
1596}
cristy86b6e552015-06-06 13:25:16 +00001597
cristy3ed852e2009-09-05 21:47:34 +00001598/*
1599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1600% %
1601% %
1602% %
1603% U n r e g i s t e r J P E G I m a g e %
1604% %
1605% %
1606% %
1607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1608%
1609% UnregisterJPEGImage() removes format registrations made by the
1610% JPEG module from the list of supported formats.
1611%
1612% The format of the UnregisterJPEGImage method is:
1613%
1614% UnregisterJPEGImage(void)
1615%
1616*/
1617ModuleExport void UnregisterJPEGImage(void)
1618{
1619 (void) UnregisterMagickInfo("PJPG");
cristya663a4f2015-01-23 12:11:57 +00001620 (void) UnregisterMagickInfo("JPS");
cristy3ed852e2009-09-05 21:47:34 +00001621 (void) UnregisterMagickInfo("JPG");
cristya663a4f2015-01-23 12:11:57 +00001622 (void) UnregisterMagickInfo("JPEG");
cristyb338b712014-11-27 14:06:34 +00001623 (void) UnregisterMagickInfo("JPE");
cristy3ed852e2009-09-05 21:47:34 +00001624}
cristy86b6e552015-06-06 13:25:16 +00001625
cristy3ed852e2009-09-05 21:47:34 +00001626#if defined(MAGICKCORE_JPEG_DELEGATE)
1627/*
1628%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629% %
1630% %
1631% %
1632% W r i t e J P E G I m a g e %
1633% %
1634% %
1635% %
1636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1637%
1638% WriteJPEGImage() writes a JPEG image file and returns it. It
1639% allocates the memory necessary for the new Image structure and returns a
1640% pointer to the new image.
1641%
1642% The format of the WriteJPEGImage method is:
1643%
cristy91044972011-04-22 14:21:16 +00001644% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001645% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001646%
1647% A description of each parameter follows:
1648%
1649% o image_info: the image info.
1650%
1651% o jpeg_image: The image.
1652%
cristy1e178e72011-08-28 19:44:34 +00001653% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001654%
1655*/
1656
cristy1b58f252012-03-01 01:41:41 +00001657static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1658{
1659 assert(table != (QuantizationTable *) NULL);
1660 if (table->slot != (char *) NULL)
1661 table->slot=DestroyString(table->slot);
1662 if (table->description != (char *) NULL)
1663 table->description=DestroyString(table->description);
1664 if (table->levels != (unsigned int *) NULL)
1665 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1666 table=(QuantizationTable *) RelinquishMagickMemory(table);
1667 return(table);
1668}
1669
cristy3ed852e2009-09-05 21:47:34 +00001670static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1671{
1672 DestinationManager
1673 *destination;
1674
1675 destination=(DestinationManager *) cinfo->dest;
1676 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1677 MaxBufferExtent,destination->buffer);
1678 if (destination->manager.free_in_buffer != MaxBufferExtent)
1679 ERREXIT(cinfo,JERR_FILE_WRITE);
1680 destination->manager.next_output_byte=destination->buffer;
1681 return(TRUE);
1682}
1683
cristy1b58f252012-03-01 01:41:41 +00001684static QuantizationTable *GetQuantizationTable(const char *filename,
1685 const char *slot,ExceptionInfo *exception)
1686{
1687 char
1688 *p,
1689 *xml;
1690
1691 const char
1692 *attribute,
1693 *content;
1694
1695 double
1696 value;
1697
1698 register ssize_t
1699 i;
1700
1701 ssize_t
1702 j;
1703
1704 QuantizationTable
1705 *table;
1706
cristy1fcc8b62012-03-02 17:25:55 +00001707 size_t
1708 length;
1709
cristy1b58f252012-03-01 01:41:41 +00001710 XMLTreeInfo
1711 *description,
1712 *levels,
1713 *quantization_tables,
1714 *table_iterator;
1715
1716 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1717 "Loading quantization tables \"%s\" ...",filename);
1718 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001719 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001720 if (xml == (char *) NULL)
1721 return(table);
1722 quantization_tables=NewXMLTree(xml,exception);
1723 if (quantization_tables == (XMLTreeInfo *) NULL)
1724 {
1725 xml=DestroyString(xml);
1726 return(table);
1727 }
1728 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1729 table_iterator != (XMLTreeInfo *) NULL;
1730 table_iterator=GetNextXMLTreeTag(table_iterator))
1731 {
1732 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1733 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1734 break;
1735 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1736 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1737 break;
1738 }
1739 if (table_iterator == (XMLTreeInfo *) NULL)
1740 {
1741 xml=DestroyString(xml);
1742 return(table);
1743 }
1744 description=GetXMLTreeChild(table_iterator,"description");
1745 if (description == (XMLTreeInfo *) NULL)
1746 {
1747 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1748 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1749 quantization_tables=DestroyXMLTree(quantization_tables);
1750 xml=DestroyString(xml);
1751 return(table);
1752 }
1753 levels=GetXMLTreeChild(table_iterator,"levels");
1754 if (levels == (XMLTreeInfo *) NULL)
1755 {
1756 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1757 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1758 quantization_tables=DestroyXMLTree(quantization_tables);
1759 xml=DestroyString(xml);
1760 return(table);
1761 }
1762 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1763 if (table == (QuantizationTable *) NULL)
1764 ThrowFatalException(ResourceLimitFatalError,
1765 "UnableToAcquireQuantizationTable");
1766 table->slot=(char *) NULL;
1767 table->description=(char *) NULL;
1768 table->levels=(unsigned int *) NULL;
1769 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1770 if (attribute != (char *) NULL)
1771 table->slot=ConstantString(attribute);
1772 content=GetXMLTreeContent(description);
1773 if (content != (char *) NULL)
1774 table->description=ConstantString(content);
1775 attribute=GetXMLTreeAttribute(levels,"width");
1776 if (attribute == (char *) NULL)
1777 {
1778 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1779 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1780 quantization_tables=DestroyXMLTree(quantization_tables);
1781 table=DestroyQuantizationTable(table);
1782 xml=DestroyString(xml);
1783 return(table);
1784 }
1785 table->width=StringToUnsignedLong(attribute);
1786 if (table->width == 0)
1787 {
1788 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1789 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1790 quantization_tables=DestroyXMLTree(quantization_tables);
1791 table=DestroyQuantizationTable(table);
1792 xml=DestroyString(xml);
1793 return(table);
1794 }
1795 attribute=GetXMLTreeAttribute(levels,"height");
1796 if (attribute == (char *) NULL)
1797 {
1798 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1799 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1800 quantization_tables=DestroyXMLTree(quantization_tables);
1801 table=DestroyQuantizationTable(table);
1802 xml=DestroyString(xml);
1803 return(table);
1804 }
1805 table->height=StringToUnsignedLong(attribute);
1806 if (table->height == 0)
1807 {
1808 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1809 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1810 quantization_tables=DestroyXMLTree(quantization_tables);
1811 table=DestroyQuantizationTable(table);
1812 xml=DestroyString(xml);
1813 return(table);
1814 }
1815 attribute=GetXMLTreeAttribute(levels,"divisor");
1816 if (attribute == (char *) NULL)
1817 {
1818 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1819 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1820 quantization_tables=DestroyXMLTree(quantization_tables);
1821 table=DestroyQuantizationTable(table);
1822 xml=DestroyString(xml);
1823 return(table);
1824 }
cristy043f3f32012-03-02 17:37:28 +00001825 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1826 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001827 {
1828 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1829 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1830 quantization_tables=DestroyXMLTree(quantization_tables);
1831 table=DestroyQuantizationTable(table);
1832 xml=DestroyString(xml);
1833 return(table);
1834 }
1835 content=GetXMLTreeContent(levels);
1836 if (content == (char *) NULL)
1837 {
1838 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1839 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1840 quantization_tables=DestroyXMLTree(quantization_tables);
1841 table=DestroyQuantizationTable(table);
1842 xml=DestroyString(xml);
1843 return(table);
1844 }
cristy1fcc8b62012-03-02 17:25:55 +00001845 length=(size_t) table->width*table->height;
1846 if (length < 64)
1847 length=64;
cristy092006b2012-03-02 17:26:02 +00001848 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001849 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001850 if (table->levels == (unsigned int *) NULL)
1851 ThrowFatalException(ResourceLimitFatalError,
1852 "UnableToAcquireQuantizationTable");
1853 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1854 {
cristy043f3f32012-03-02 17:37:28 +00001855 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1856 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001857 while (isspace((int) ((unsigned char) *p)) != 0)
1858 p++;
1859 if (*p == ',')
1860 p++;
1861 content=p;
1862 }
cristy043f3f32012-03-02 17:37:28 +00001863 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001864 (void) value;
1865 if (p != content)
1866 {
1867 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1868 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1869 quantization_tables=DestroyXMLTree(quantization_tables);
1870 table=DestroyQuantizationTable(table);
1871 xml=DestroyString(xml);
1872 return(table);
1873 }
cristy043f3f32012-03-02 17:37:28 +00001874 for (j=i; j < 64; j++)
1875 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001876 quantization_tables=DestroyXMLTree(quantization_tables);
1877 xml=DestroyString(xml);
1878 return(table);
1879}
1880
cristy3ed852e2009-09-05 21:47:34 +00001881static void InitializeDestination(j_compress_ptr cinfo)
1882{
1883 DestinationManager
1884 *destination;
1885
1886 destination=(DestinationManager *) cinfo->dest;
1887 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1888 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1889 destination->manager.next_output_byte=destination->buffer;
1890 destination->manager.free_in_buffer=MaxBufferExtent;
1891}
1892
cristy3ed852e2009-09-05 21:47:34 +00001893static void TerminateDestination(j_compress_ptr cinfo)
1894{
1895 DestinationManager
1896 *destination;
1897
1898 destination=(DestinationManager *) cinfo->dest;
1899 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1900 {
1901 ssize_t
1902 count;
1903
1904 count=WriteBlob(destination->image,MaxBufferExtent-
1905 destination->manager.free_in_buffer,destination->buffer);
1906 if (count != (ssize_t)
1907 (MaxBufferExtent-destination->manager.free_in_buffer))
1908 ERREXIT(cinfo,JERR_FILE_WRITE);
1909 }
1910}
1911
1912static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1913{
1914 const char
1915 *name;
1916
1917 const StringInfo
1918 *profile;
1919
1920 MagickBooleanType
1921 iptc;
1922
cristybb503372010-05-27 20:51:26 +00001923 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001924 i;
1925
1926 size_t
cristy524222d2011-04-25 00:37:06 +00001927 length,
1928 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001929
1930 StringInfo
1931 *custom_profile;
1932
cristy3ed852e2009-09-05 21:47:34 +00001933 /*
1934 Save image profile as a APP marker.
1935 */
1936 iptc=MagickFalse;
1937 custom_profile=AcquireStringInfo(65535L);
1938 ResetImageProfileIterator(image);
1939 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1940 {
1941 profile=GetImageProfile(image,name);
1942 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001943 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001944 {
1945 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1946 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1947 (unsigned int) length);
1948 }
1949 if (LocaleCompare(name,"ICC") == 0)
1950 {
1951 register unsigned char
1952 *p;
1953
cristy8ba82ae2013-05-29 14:19:49 +00001954 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001955 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001956 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001957 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001958 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001959 {
1960 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001961 p[12]=(unsigned char) ((i/65519L)+1);
1962 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001963 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001964 length);
1965 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001966 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001967 }
1968 }
1969 if (((LocaleCompare(name,"IPTC") == 0) ||
1970 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1971 {
dirkc93932a2015-10-01 16:56:10 +02001972 register unsigned char
1973 *p;
1974
cristybb503372010-05-27 20:51:26 +00001975 size_t
cristy3ed852e2009-09-05 21:47:34 +00001976 roundup;
1977
1978 iptc=MagickTrue;
dirkc93932a2015-10-01 16:56:10 +02001979 p=GetStringInfoDatum(custom_profile);
cristybb503372010-05-27 20:51:26 +00001980 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001981 {
1982 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001983 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001984 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1985 {
1986 (void) memcpy(p,"Photoshop 3.0 ",14);
1987 tag_length=14;
1988 }
1989 else
1990 {
1991 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1992 tag_length=26;
1993 p[24]=(unsigned char) (length >> 8);
1994 p[25]=(unsigned char) (length & 0xff);
1995 }
1996 p[13]=0x00;
1997 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001998 if (roundup != 0)
1999 p[length+tag_length]='\0';
2000 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
2001 custom_profile),(unsigned int) (length+tag_length+roundup));
2002 }
2003 }
2004 if (LocaleCompare(name,"XMP") == 0)
2005 {
2006 StringInfo
2007 *xmp_profile;
2008
2009 /*
2010 Add namespace to XMP profile.
2011 */
cristy0615f0e2011-10-12 11:36:46 +00002012 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy30841e62014-05-19 00:45:15 +00002013 if (xmp_profile != (StringInfo *) NULL)
cristy8418c7e2014-05-18 18:38:26 +00002014 {
cristyc08a0aa2014-05-25 23:30:09 +00002015 if (profile != (StringInfo *) NULL)
2016 ConcatenateStringInfo(xmp_profile,profile);
cristy8418c7e2014-05-18 18:38:26 +00002017 GetStringInfoDatum(xmp_profile)[28]='\0';
2018 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
2019 {
2020 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
2021 jpeg_write_marker(jpeg_info,XML_MARKER,
2022 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
2023 }
2024 xmp_profile=DestroyStringInfo(xmp_profile);
2025 }
cristy3ed852e2009-09-05 21:47:34 +00002026 }
cristye8c25f92010-06-03 00:53:06 +00002027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2028 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00002029 name=GetNextImageProfile(image);
2030 }
2031 custom_profile=DestroyStringInfo(custom_profile);
2032}
2033
2034static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
2035{
2036 DestinationManager
2037 *destination;
2038
2039 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
2040 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
2041 destination=(DestinationManager *) cinfo->dest;
2042 destination->manager.init_destination=InitializeDestination;
2043 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2044 destination->manager.term_destination=TerminateDestination;
2045 destination->image=image;
2046}
2047
2048static char **SamplingFactorToList(const char *text)
2049{
2050 char
2051 **textlist;
2052
2053 register char
2054 *q;
2055
2056 register const char
2057 *p;
2058
cristybb503372010-05-27 20:51:26 +00002059 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002060 i;
2061
cristy3ed852e2009-09-05 21:47:34 +00002062 if (text == (char *) NULL)
2063 return((char **) NULL);
2064 /*
2065 Convert string to an ASCII list.
2066 */
Cristy71406342015-09-11 19:49:21 -04002067 textlist=(char **) AcquireQuantumMemory((size_t) MAX_COMPONENTS,
cristy3ed852e2009-09-05 21:47:34 +00002068 sizeof(*textlist));
2069 if (textlist == (char **) NULL)
2070 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2071 p=text;
Cristy71406342015-09-11 19:49:21 -04002072 for (i=0; i < (ssize_t) MAX_COMPONENTS; i++)
cristy3ed852e2009-09-05 21:47:34 +00002073 {
2074 for (q=(char *) p; *q != '\0'; q++)
2075 if (*q == ',')
2076 break;
cristy151b66d2015-04-15 10:50:31 +00002077 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00002078 sizeof(*textlist[i]));
2079 if (textlist[i] == (char *) NULL)
2080 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2081 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2082 if (*q == '\r')
2083 q++;
Cristy71406342015-09-11 19:49:21 -04002084 if (*q == '\0')
2085 break;
cristy3ed852e2009-09-05 21:47:34 +00002086 p=q+1;
2087 }
Cristy71406342015-09-11 19:49:21 -04002088 for (i++; i < (ssize_t) MAX_COMPONENTS; i++)
2089 textlist[i]=ConstantString("1x1");
cristy3ed852e2009-09-05 21:47:34 +00002090 return(textlist);
2091}
2092
2093static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002094 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002095{
2096 const char
2097 *option,
2098 *sampling_factor,
2099 *value;
2100
2101 ErrorManager
2102 error_manager;
2103
cristy86b6e552015-06-06 13:25:16 +00002104 Image
2105 *volatile volatile_image;
2106
cristy21749e92012-02-18 02:29:21 +00002107 int
cristy7b8ed292013-05-18 01:13:56 +00002108 colorspace,
cristy21749e92012-02-18 02:29:21 +00002109 quality;
2110
cristy3ed852e2009-09-05 21:47:34 +00002111 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002112 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002113
2114 JSAMPROW
2115 scanline[1];
2116
cristy3ed852e2009-09-05 21:47:34 +00002117 MagickBooleanType
2118 status;
2119
cristy22646e22013-06-23 16:34:03 +00002120 MemoryInfo
2121 *memory_info;
2122
cristy3ed852e2009-09-05 21:47:34 +00002123 register JSAMPLE
2124 *q;
2125
cristybb503372010-05-27 20:51:26 +00002126 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002127 i;
2128
cristy524222d2011-04-25 00:37:06 +00002129 ssize_t
2130 y;
2131
cristy3ed852e2009-09-05 21:47:34 +00002132 struct jpeg_compress_struct
2133 jpeg_info;
2134
2135 struct jpeg_error_mgr
2136 jpeg_error;
2137
cristy5328a4c2013-12-03 11:32:13 +00002138 unsigned short
2139 scale;
2140
cristy3ed852e2009-09-05 21:47:34 +00002141 /*
2142 Open image file.
2143 */
2144 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002145 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002146 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002147 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002148 if (image->debug != MagickFalse)
2149 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002150 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002151 assert(exception->signature == MagickCoreSignature);
cristya663a4f2015-01-23 12:11:57 +00002152 if ((LocaleCompare(image_info->magick,"JPS") == 0) &&
2153 (image->next != (Image *) NULL))
2154 image=AppendImages(image,MagickFalse,exception);
cristy73ef4eb2015-01-23 14:31:57 +00002155 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2156 if (status == MagickFalse)
2157 return(status);
cristy3ed852e2009-09-05 21:47:34 +00002158 /*
2159 Initialize JPEG parameters.
2160 */
cristy91044972011-04-22 14:21:16 +00002161 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002162 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2163 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
cristy86b6e552015-06-06 13:25:16 +00002164 volatile_image=image;
2165 jpeg_info.client_data=(void *) volatile_image;
cristy3ed852e2009-09-05 21:47:34 +00002166 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002167 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002168 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002169 error_manager.exception=exception;
cristy86b6e552015-06-06 13:25:16 +00002170 error_manager.image=volatile_image;
cristy72f87f82013-06-23 16:43:05 +00002171 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002172 if (setjmp(error_manager.error_recovery) != 0)
2173 {
2174 jpeg_destroy_compress(&jpeg_info);
cristy86b6e552015-06-06 13:25:16 +00002175 (void) CloseBlob(volatile_image);
cristy3ed852e2009-09-05 21:47:34 +00002176 return(MagickFalse);
2177 }
2178 jpeg_info.client_data=(void *) &error_manager;
2179 jpeg_create_compress(&jpeg_info);
2180 JPEGDestinationManager(&jpeg_info,image);
2181 if ((image->columns != (unsigned int) image->columns) ||
2182 (image->rows != (unsigned int) image->rows))
2183 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2184 jpeg_info.image_width=(unsigned int) image->columns;
2185 jpeg_info.image_height=(unsigned int) image->rows;
2186 jpeg_info.input_components=3;
2187 jpeg_info.data_precision=8;
2188 jpeg_info.in_color_space=JCS_RGB;
2189 switch (image->colorspace)
2190 {
2191 case CMYKColorspace:
2192 {
2193 jpeg_info.input_components=4;
2194 jpeg_info.in_color_space=JCS_CMYK;
2195 break;
2196 }
2197 case YCbCrColorspace:
2198 case Rec601YCbCrColorspace:
2199 case Rec709YCbCrColorspace:
2200 {
2201 jpeg_info.in_color_space=JCS_YCbCr;
2202 break;
2203 }
2204 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002205 {
cristye9355552014-01-16 13:35:05 +00002206 if (image_info->type == TrueColorType)
2207 break;
cristy3ed852e2009-09-05 21:47:34 +00002208 jpeg_info.input_components=1;
2209 jpeg_info.in_color_space=JCS_GRAYSCALE;
2210 break;
2211 }
2212 default:
cristy9f396782009-12-21 01:37:45 +00002213 {
cristyaf8d3912014-02-21 14:50:33 +00002214 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye9355552014-01-16 13:35:05 +00002215 if (image_info->type == TrueColorType)
2216 break;
dirkf1d85482015-04-06 00:36:00 +00002217 if (SetImageGray(image,exception) != MagickFalse)
cristye9355552014-01-16 13:35:05 +00002218 {
2219 jpeg_info.input_components=1;
2220 jpeg_info.in_color_space=JCS_GRAYSCALE;
2221 }
cristy3ed852e2009-09-05 21:47:34 +00002222 break;
cristy9f396782009-12-21 01:37:45 +00002223 }
cristy3ed852e2009-09-05 21:47:34 +00002224 }
cristy3ed852e2009-09-05 21:47:34 +00002225 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002226 if (jpeg_info.in_color_space == JCS_CMYK)
2227 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002228 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2229 jpeg_info.data_precision=8;
2230 else
dirk93b02b72013-11-16 16:03:36 +00002231 jpeg_info.data_precision=BITS_IN_JSAMPLE;
cristy3ed852e2009-09-05 21:47:34 +00002232 if (image->debug != MagickFalse)
2233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
Cristy39b28d32015-09-29 08:20:12 -04002234 "Image resolution: %.20g,%.20g",image->resolution.x,image->resolution.y);
cristy2a11bef2011-10-28 18:33:11 +00002235 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002236 {
2237 /*
2238 Set image resolution.
2239 */
cristydf878012014-01-12 23:39:40 +00002240 jpeg_info.write_JFIF_header=TRUE;
Cristy39b28d32015-09-29 08:20:12 -04002241 jpeg_info.X_density=(UINT16) image->resolution.x;
2242 jpeg_info.Y_density=(UINT16) image->resolution.y;
cristy0c220632013-09-25 22:10:28 +00002243 /*
2244 Set image resolution units.
2245 */
cristy3ed852e2009-09-05 21:47:34 +00002246 if (image->units == PixelsPerInchResolution)
2247 jpeg_info.density_unit=(UINT8) 1;
2248 if (image->units == PixelsPerCentimeterResolution)
2249 jpeg_info.density_unit=(UINT8) 2;
2250 }
cristy97cb3bf2012-02-21 18:31:20 +00002251 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002252 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002253 if (option != (const char *) NULL)
2254 switch (*option)
2255 {
2256 case 'D':
2257 case 'd':
2258 {
2259 if (LocaleCompare(option,"default") == 0)
2260 jpeg_info.dct_method=JDCT_DEFAULT;
2261 break;
2262 }
2263 case 'F':
2264 case 'f':
2265 {
2266 if (LocaleCompare(option,"fastest") == 0)
2267 jpeg_info.dct_method=JDCT_FASTEST;
2268 if (LocaleCompare(option,"float") == 0)
2269 jpeg_info.dct_method=JDCT_FLOAT;
2270 break;
2271 }
2272 case 'I':
2273 case 'i':
2274 {
2275 if (LocaleCompare(option,"ifast") == 0)
2276 jpeg_info.dct_method=JDCT_IFAST;
2277 if (LocaleCompare(option,"islow") == 0)
2278 jpeg_info.dct_method=JDCT_ISLOW;
2279 break;
2280 }
2281 }
cristy092ec8d2013-04-26 13:46:22 +00002282 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002283 if (option != (const char *) NULL)
cristyb9d0e7e2014-11-02 18:14:44 +00002284 jpeg_info.optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE :
2285 FALSE;
cristy7d31f6e2014-11-11 13:07:58 +00002286 else
2287 {
2288 MagickSizeType
2289 length;
2290
2291 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2292 image->rows*sizeof(JSAMPLE);
2293 if (length == (MagickSizeType) ((size_t) length))
2294 {
2295 /*
2296 Perform optimization only if available memory resources permit it.
2297 */
2298 status=AcquireMagickResource(MemoryResource,length);
2299 RelinquishMagickResource(MemoryResource,length);
2300 jpeg_info.optimize_coding=status == MagickFalse ? FALSE : TRUE;
2301 }
2302 }
cristy3ed852e2009-09-05 21:47:34 +00002303#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2304 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2305 (image_info->interlace != NoInterlace))
2306 {
2307 if (image->debug != MagickFalse)
2308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2309 "Interlace: progressive");
2310 jpeg_simple_progression(&jpeg_info);
2311 }
2312 else
2313 if (image->debug != MagickFalse)
2314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2315 "Interlace: non-progressive");
2316#else
2317 if (image->debug != MagickFalse)
2318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2319 "Interlace: nonprogressive");
2320#endif
dirk871abab2013-11-03 13:21:16 +00002321 quality=92;
cristy2a950402014-03-12 17:57:14 +00002322 if ((image_info->compression != LosslessJPEGCompression) &&
2323 (image->quality <= 100))
2324 {
2325 if (image->quality != UndefinedCompressionQuality)
2326 quality=(int) image->quality;
2327 if (image->debug != MagickFalse)
2328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2329 (double) image->quality);
2330 }
2331 else
2332 {
2333#if !defined(C_LOSSLESS_SUPPORTED)
2334 quality=100;
2335 if (image->debug != MagickFalse)
2336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2337#else
2338 if (image->quality < 100)
2339 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2340 "LosslessToLossyJPEGConversion",image->filename);
2341 else
2342 {
2343 int
2344 point_transform,
2345 predictor;
2346
2347 predictor=image->quality/100; /* range 1-7 */
2348 point_transform=image->quality % 20; /* range 0-15 */
2349 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2350 if (image->debug != MagickFalse)
2351 {
2352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2353 "Compression: lossless");
2354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355 "Predictor: %d",predictor);
2356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2357 "Point Transform: %d",point_transform);
2358 }
2359 }
2360#endif
2361 }
cristy092ec8d2013-04-26 13:46:22 +00002362 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002363 if (option != (const char *) NULL)
2364 {
2365 Image
2366 *jpeg_image;
2367
2368 ImageInfo
dirkc93932a2015-10-01 16:56:10 +02002369 *extent_info;
cristy0adb4f92009-11-28 18:08:51 +00002370
dirkc93932a2015-10-01 16:56:10 +02002371 extent_info=CloneImageInfo(image_info);
2372 extent_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002373 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002374 if (jpeg_image != (Image *) NULL)
2375 {
2376 MagickSizeType
2377 extent;
2378
2379 size_t
cristy87e73ab2010-02-12 01:59:12 +00002380 maximum,
2381 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002382
2383 /*
2384 Search for compression quality that does not exceed image extent.
2385 */
dirkc93932a2015-10-01 16:56:10 +02002386 extent_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002387 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
dirkc93932a2015-10-01 16:56:10 +02002388 (void) DeleteImageOption(extent_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002389 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
cristy4e8b2ed2015-03-06 19:54:18 +00002390 maximum=image->quality;
2391 if (maximum < 2)
2392 maximum=2;
dirkcf845242013-08-11 21:36:19 +00002393 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002394 {
cristy3975f402014-02-26 14:36:33 +00002395 (void) AcquireUniqueFilename(jpeg_image->filename);
2396 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
dirkc93932a2015-10-01 16:56:10 +02002397 status=WriteJPEGImage(extent_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002398 if (GetBlobSize(jpeg_image) <= extent)
cristy3975f402014-02-26 14:36:33 +00002399 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002400 else
cristy3975f402014-02-26 14:36:33 +00002401 maximum=jpeg_image->quality-1;
2402 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristy0adb4f92009-11-28 18:08:51 +00002403 }
cristy58d4fe12015-02-07 11:53:05 +00002404 quality=(int) minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002405 jpeg_image=DestroyImage(jpeg_image);
2406 }
dirkc93932a2015-10-01 16:56:10 +02002407 extent_info=DestroyImageInfo(extent_info);
cristy0adb4f92009-11-28 18:08:51 +00002408 }
cristy9bb7c842014-06-17 23:42:24 +00002409 jpeg_set_quality(&jpeg_info,quality,TRUE);
cristy16e74722012-07-01 13:01:38 +00002410#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002411 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002412 if (option != (const char *) NULL)
2413 {
2414 GeometryInfo
2415 geometry_info;
2416
2417 int
2418 flags;
2419
2420 /*
2421 Set quality scaling for luminance and chrominance separately.
2422 */
2423 flags=ParseGeometry(option,&geometry_info);
2424 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2425 {
2426 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2427 (geometry_info.rho+0.5));
2428 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2429 (geometry_info.sigma+0.5));
cristy9bb7c842014-06-17 23:42:24 +00002430 jpeg_default_qtables(&jpeg_info,TRUE);
cristy16e74722012-07-01 13:01:38 +00002431 }
2432 }
2433#endif
cristy7b8ed292013-05-18 01:13:56 +00002434 colorspace=jpeg_info.in_color_space;
2435 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002436 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002437 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002438 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002439 colorspace=StringToInteger(value);
2440 sampling_factor=(const char *) NULL;
2441 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002442 {
cristy7b8ed292013-05-18 01:13:56 +00002443 value=GetImageOption(image_info,"jpeg:sampling-factor");
2444 if (value == (char *) NULL)
2445 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2446 if (value != (char *) NULL)
2447 {
2448 sampling_factor=value;
2449 if (image->debug != MagickFalse)
2450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2451 " Input sampling-factors=%s",sampling_factor);
2452 }
cristy3ed852e2009-09-05 21:47:34 +00002453 }
cristy7b8ed292013-05-18 01:13:56 +00002454 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002455 if (image_info->sampling_factor != (char *) NULL)
2456 sampling_factor=image_info->sampling_factor;
2457 if (sampling_factor == (const char *) NULL)
2458 {
cristyc243f622015-02-21 17:20:34 +00002459 if (quality >= 90)
cristy3ed852e2009-09-05 21:47:34 +00002460 for (i=0; i < MAX_COMPONENTS; i++)
2461 {
2462 jpeg_info.comp_info[i].h_samp_factor=1;
2463 jpeg_info.comp_info[i].v_samp_factor=1;
2464 }
2465 }
2466 else
2467 {
2468 char
2469 **factors;
2470
2471 GeometryInfo
2472 geometry_info;
2473
2474 MagickStatusType
2475 flags;
2476
2477 /*
2478 Set sampling factor.
2479 */
2480 i=0;
2481 factors=SamplingFactorToList(sampling_factor);
2482 if (factors != (char **) NULL)
2483 {
2484 for (i=0; i < MAX_COMPONENTS; i++)
2485 {
2486 if (factors[i] == (char *) NULL)
2487 break;
2488 flags=ParseGeometry(factors[i],&geometry_info);
2489 if ((flags & SigmaValue) == 0)
2490 geometry_info.sigma=geometry_info.rho;
2491 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2492 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2493 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2494 }
2495 factors=(char **) RelinquishMagickMemory(factors);
2496 }
2497 for ( ; i < MAX_COMPONENTS; i++)
2498 {
2499 jpeg_info.comp_info[i].h_samp_factor=1;
2500 jpeg_info.comp_info[i].v_samp_factor=1;
2501 }
2502 }
cristy092ec8d2013-04-26 13:46:22 +00002503 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002504 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002505 {
cristy1b58f252012-03-01 01:41:41 +00002506 QuantizationTable
2507 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002508
cristy1445b322012-02-19 18:57:30 +00002509 /*
cristy1b58f252012-03-01 01:41:41 +00002510 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002511 */
cristy1b58f252012-03-01 01:41:41 +00002512 table=GetQuantizationTable(option,"0",exception);
2513 if (table != (QuantizationTable *) NULL)
2514 {
cristyae453b72013-04-21 22:24:20 +00002515 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002516 jpeg_info.comp_info[i].quant_tbl_no=0;
2517 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2518 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002519 table=DestroyQuantizationTable(table);
2520 }
2521 table=GetQuantizationTable(option,"1",exception);
2522 if (table != (QuantizationTable *) NULL)
2523 {
cristyae453b72013-04-21 22:24:20 +00002524 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002525 jpeg_info.comp_info[i].quant_tbl_no=1;
2526 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2527 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002528 table=DestroyQuantizationTable(table);
2529 }
2530 table=GetQuantizationTable(option,"2",exception);
2531 if (table != (QuantizationTable *) NULL)
2532 {
cristyae453b72013-04-21 22:24:20 +00002533 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002534 jpeg_info.comp_info[i].quant_tbl_no=2;
2535 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2536 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002537 table=DestroyQuantizationTable(table);
2538 }
2539 table=GetQuantizationTable(option,"3",exception);
2540 if (table != (QuantizationTable *) NULL)
2541 {
cristyae453b72013-04-21 22:24:20 +00002542 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002543 jpeg_info.comp_info[i].quant_tbl_no=3;
2544 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2545 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002546 table=DestroyQuantizationTable(table);
2547 }
cristy1445b322012-02-19 18:57:30 +00002548 }
cristy9bb7c842014-06-17 23:42:24 +00002549 jpeg_start_compress(&jpeg_info,TRUE);
cristy3ed852e2009-09-05 21:47:34 +00002550 if (image->debug != MagickFalse)
2551 {
2552 if (image->storage_class == PseudoClass)
2553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2554 "Storage class: PseudoClass");
2555 else
2556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2557 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2559 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002560 if (image->colors != 0)
2561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002562 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002563 else
2564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2565 "Number of colors: unspecified");
2566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2567 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2568 switch (image->colorspace)
2569 {
2570 case CMYKColorspace:
2571 {
2572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2573 "Storage class: DirectClass");
2574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2575 "Colorspace: CMYK");
2576 break;
2577 }
2578 case YCbCrColorspace:
2579 case Rec601YCbCrColorspace:
2580 case Rec709YCbCrColorspace:
2581 {
2582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2583 "Colorspace: YCbCr");
2584 break;
2585 }
2586 default:
2587 break;
2588 }
2589 switch (image->colorspace)
2590 {
2591 case CMYKColorspace:
2592 {
2593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2594 "Colorspace: CMYK");
2595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2596 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2597 jpeg_info.comp_info[0].h_samp_factor,
2598 jpeg_info.comp_info[0].v_samp_factor,
2599 jpeg_info.comp_info[1].h_samp_factor,
2600 jpeg_info.comp_info[1].v_samp_factor,
2601 jpeg_info.comp_info[2].h_samp_factor,
2602 jpeg_info.comp_info[2].v_samp_factor,
2603 jpeg_info.comp_info[3].h_samp_factor,
2604 jpeg_info.comp_info[3].v_samp_factor);
2605 break;
2606 }
2607 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002608 {
2609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2610 "Colorspace: GRAY");
2611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2612 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2613 jpeg_info.comp_info[0].v_samp_factor);
2614 break;
2615 }
cristyed8d7852013-08-01 22:44:22 +00002616 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002617 case RGBColorspace:
2618 {
2619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002620 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2622 "Sampling factors: %dx%d,%dx%d,%dx%d",
2623 jpeg_info.comp_info[0].h_samp_factor,
2624 jpeg_info.comp_info[0].v_samp_factor,
2625 jpeg_info.comp_info[1].h_samp_factor,
2626 jpeg_info.comp_info[1].v_samp_factor,
2627 jpeg_info.comp_info[2].h_samp_factor,
2628 jpeg_info.comp_info[2].v_samp_factor);
2629 break;
2630 }
2631 case YCbCrColorspace:
2632 case Rec601YCbCrColorspace:
2633 case Rec709YCbCrColorspace:
2634 {
2635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2636 "Colorspace: YCbCr");
2637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2638 "Sampling factors: %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 break;
2646 }
2647 default:
2648 {
2649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2650 image->colorspace);
2651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2652 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2653 jpeg_info.comp_info[0].h_samp_factor,
2654 jpeg_info.comp_info[0].v_samp_factor,
2655 jpeg_info.comp_info[1].h_samp_factor,
2656 jpeg_info.comp_info[1].v_samp_factor,
2657 jpeg_info.comp_info[2].h_samp_factor,
2658 jpeg_info.comp_info[2].v_samp_factor,
2659 jpeg_info.comp_info[3].h_samp_factor,
2660 jpeg_info.comp_info[3].v_samp_factor);
2661 break;
2662 }
2663 }
2664 }
2665 /*
2666 Write JPEG profiles.
2667 */
cristyd15e6592011-10-15 00:13:06 +00002668 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002669 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002670 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002671 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2672 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2673 if (image->profiles != (void *) NULL)
2674 WriteProfile(&jpeg_info,image);
2675 /*
2676 Convert MIFF to JPEG raster pixels.
2677 */
cristy22646e22013-06-23 16:34:03 +00002678 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002679 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002680 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002681 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002682 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002683 if (setjmp(error_manager.error_recovery) != 0)
2684 {
2685 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002686 if (memory_info != (MemoryInfo *) NULL)
2687 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002688 (void) CloseBlob(image);
2689 return(MagickFalse);
2690 }
2691 scanline[0]=(JSAMPROW) jpeg_pixels;
Cristy71406342015-09-11 19:49:21 -04002692 scale=65535/(unsigned short) GetQuantumRange((size_t)
2693 jpeg_info.data_precision);
cristy31db6c12013-12-04 15:17:30 +00002694 if (scale == 0)
Cristy71406342015-09-11 19:49:21 -04002695 scale=1;
cristye90d7402010-03-14 18:21:29 +00002696 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002697 {
cristy3ed852e2009-09-05 21:47:34 +00002698 if ((jpeg_info.in_color_space == JCS_RGB) ||
2699 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002700 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002701 {
cristy4c08aed2011-07-01 19:47:50 +00002702 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002703 *p;
2704
cristybb503372010-05-27 20:51:26 +00002705 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002706 x;
2707
cristy1e178e72011-08-28 19:44:34 +00002708 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002709 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002710 break;
2711 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002712 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002713 {
cristy4c08aed2011-07-01 19:47:50 +00002714 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2715 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2716 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002717 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002718 }
2719 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002720 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2721 image->rows);
2722 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002723 break;
2724 }
2725 else
cristye90d7402010-03-14 18:21:29 +00002726 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002727 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002728 {
cristyd0323222013-04-07 16:13:21 +00002729 register const Quantum
2730 *p;
2731
2732 register ssize_t
2733 x;
2734
2735 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2736 if (p == (const Quantum *) NULL)
2737 break;
2738 q=jpeg_pixels;
2739 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002740 {
cristyd0323222013-04-07 16:13:21 +00002741 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2742 image,p)));
2743 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002744 }
cristyd0323222013-04-07 16:13:21 +00002745 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2746 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2747 image->rows);
2748 if (status == MagickFalse)
2749 break;
2750 }
cristye90d7402010-03-14 18:21:29 +00002751 else
cristybb503372010-05-27 20:51:26 +00002752 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002753 {
cristy4c08aed2011-07-01 19:47:50 +00002754 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002755 *p;
2756
cristybb503372010-05-27 20:51:26 +00002757 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002758 x;
2759
cristy1e178e72011-08-28 19:44:34 +00002760 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002761 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002762 break;
2763 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002764 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002765 {
2766 /*
2767 Convert DirectClass packets to contiguous CMYK scanlines.
2768 */
2769 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002770 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002771 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002772 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002773 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002774 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002775 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002776 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002777 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002778 }
2779 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002780 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2781 image->rows);
2782 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002783 break;
2784 }
2785 }
2786 else
2787 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002788 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002789 {
cristy4c08aed2011-07-01 19:47:50 +00002790 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002791 *p;
2792
cristybb503372010-05-27 20:51:26 +00002793 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002794 x;
2795
cristy1e178e72011-08-28 19:44:34 +00002796 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002797 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002798 break;
2799 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002800 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002801 {
cristy217ddd62013-12-06 23:27:09 +00002802 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2803 p)))/scale);
cristyed231572011-07-14 02:18:59 +00002804 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002805 }
2806 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002807 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2808 image->rows);
2809 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002810 break;
2811 }
2812 else
2813 if ((jpeg_info.in_color_space == JCS_RGB) ||
2814 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002815 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002816 {
cristy4c08aed2011-07-01 19:47:50 +00002817 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002818 *p;
2819
cristybb503372010-05-27 20:51:26 +00002820 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002821 x;
2822
cristy1e178e72011-08-28 19:44:34 +00002823 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002824 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002825 break;
2826 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002827 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002828 {
cristy5328a4c2013-12-03 11:32:13 +00002829 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2830 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2831 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002832 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002833 }
2834 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002835 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002836 image->rows);
cristye90d7402010-03-14 18:21:29 +00002837 if (status == MagickFalse)
2838 break;
2839 }
2840 else
cristybb503372010-05-27 20:51:26 +00002841 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002842 {
cristy4c08aed2011-07-01 19:47:50 +00002843 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002844 *p;
2845
cristybb503372010-05-27 20:51:26 +00002846 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002847 x;
2848
cristy1e178e72011-08-28 19:44:34 +00002849 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002850 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002851 break;
2852 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002853 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002854 {
2855 /*
2856 Convert DirectClass packets to contiguous CMYK scanlines.
2857 */
cristy217ddd62013-12-06 23:27:09 +00002858 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2859 image,p))/scale);
2860 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2861 image,p))/scale);
2862 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2863 image,p))/scale);
2864 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2865 image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002866 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002867 }
2868 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002869 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002870 image->rows);
cristye90d7402010-03-14 18:21:29 +00002871 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002872 break;
2873 }
cristybb503372010-05-27 20:51:26 +00002874 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002875 jpeg_finish_compress(&jpeg_info);
2876 /*
2877 Relinquish resources.
2878 */
2879 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002880 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002881 (void) CloseBlob(image);
2882 return(MagickTrue);
2883}
2884#endif