blob: 73f6e49d8a50431ce0fee542bb5b6d96bbd1ecba [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,
dirk751a6672016-03-19 11:09:53 +0100796 Image *image)
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");
Cristy2a4877a2016-05-07 20:08:57 -04001264 if ((jpeg_info.output_components == 1) && (jpeg_info.quantize_colors == 0))
cristy3ed852e2009-09-05 21:47:34 +00001265 {
cristybb503372010-05-27 20:51:26 +00001266 size_t
cristy3ed852e2009-09-05 21:47:34 +00001267 colors;
1268
cristybb503372010-05-27 20:51:26 +00001269 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001270 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001271 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1272 }
1273 if (image->debug != MagickFalse)
1274 {
1275 if (image->interlace != NoInterlace)
1276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1277 "Interlace: progressive");
1278 else
1279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1280 "Interlace: nonprogressive");
1281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1282 (int) jpeg_info.data_precision);
1283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1284 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1285 }
dirk751a6672016-03-19 11:09:53 +01001286 JPEGSetImageQuality(&jpeg_info,image);
cristyd15e6592011-10-15 00:13:06 +00001287 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristy151b66d2015-04-15 10:50:31 +00001288 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001289 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001290 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001291 if (image_info->ping != MagickFalse)
1292 {
1293 jpeg_destroy_decompress(&jpeg_info);
1294 (void) CloseBlob(image);
1295 return(GetFirstImageInList(image));
1296 }
cristyacabb842014-12-14 23:36:33 +00001297 status=SetImageExtent(image,image->columns,image->rows,exception);
1298 if (status == MagickFalse)
cristy457f81d2015-02-01 18:13:23 +00001299 {
1300 jpeg_destroy_decompress(&jpeg_info);
1301 return(DestroyImageList(image));
1302 }
1303 if ((jpeg_info.output_components != 1) &&
1304 (jpeg_info.output_components != 3) && (jpeg_info.output_components != 4))
1305 {
1306 jpeg_destroy_decompress(&jpeg_info);
1307 ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
1308 }
cristy22646e22013-06-23 16:34:03 +00001309 memory_info=AcquireVirtualMemory((size_t) image->columns,
1310 jpeg_info.output_components*sizeof(*jpeg_pixels));
1311 if (memory_info == (MemoryInfo *) NULL)
cristyee1bdaa2013-07-16 16:45:03 +00001312 {
1313 jpeg_destroy_decompress(&jpeg_info);
1314 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1315 }
cristy22646e22013-06-23 16:34:03 +00001316 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001317 /*
1318 Convert JPEG pixels to pixel packets.
1319 */
1320 if (setjmp(error_manager.error_recovery) != 0)
1321 {
cristy22646e22013-06-23 16:34:03 +00001322 if (memory_info != (MemoryInfo *) NULL)
1323 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001324 jpeg_destroy_decompress(&jpeg_info);
1325 (void) CloseBlob(image);
1326 number_pixels=(MagickSizeType) image->columns*image->rows;
1327 if (number_pixels != 0)
1328 return(GetFirstImageInList(image));
1329 return(DestroyImage(image));
1330 }
Cristy2a4877a2016-05-07 20:08:57 -04001331 if (jpeg_info.quantize_colors != 0)
cristy3ed852e2009-09-05 21:47:34 +00001332 {
cristybb503372010-05-27 20:51:26 +00001333 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001334 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001335 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001336 {
cristy1b58f252012-03-01 01:41:41 +00001337 image->colormap[i].red=(double) ScaleCharToQuantum(
1338 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001339 image->colormap[i].green=image->colormap[i].red;
1340 image->colormap[i].blue=image->colormap[i].red;
Cristy71406342015-09-11 19:49:21 -04001341 image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001342 }
1343 else
cristybb503372010-05-27 20:51:26 +00001344 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001345 {
cristy1b58f252012-03-01 01:41:41 +00001346 image->colormap[i].red=(double) ScaleCharToQuantum(
1347 jpeg_info.colormap[0][i]);
1348 image->colormap[i].green=(double) ScaleCharToQuantum(
1349 jpeg_info.colormap[1][i]);
1350 image->colormap[i].blue=(double) ScaleCharToQuantum(
1351 jpeg_info.colormap[2][i]);
Cristy71406342015-09-11 19:49:21 -04001352 image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001353 }
1354 }
1355 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001356 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001357 {
cristybb503372010-05-27 20:51:26 +00001358 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001359 x;
1360
cristy4c08aed2011-07-01 19:47:50 +00001361 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001362 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001363
1364 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1365 {
1366 (void) ThrowMagickException(exception,GetMagickModule(),
1367 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1368 continue;
1369 }
1370 p=jpeg_pixels;
1371 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001372 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001373 break;
cristy3ed852e2009-09-05 21:47:34 +00001374 if (jpeg_info.data_precision > 8)
1375 {
cristy5328a4c2013-12-03 11:32:13 +00001376 unsigned short
1377 scale;
1378
Cristy71406342015-09-11 19:49:21 -04001379 scale=65535/(unsigned short) GetQuantumRange((size_t)
1380 jpeg_info.data_precision);
cristy3ed852e2009-09-05 21:47:34 +00001381 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001382 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001383 {
Cristy71406342015-09-11 19:49:21 -04001384 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001385 pixel;
1386
Cristy71406342015-09-11 19:49:21 -04001387 pixel=(ssize_t) (scale*GETJSAMPLE(*p));
1388 index=(Quantum) ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001389 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001390 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001391 p++;
cristyed231572011-07-14 02:18:59 +00001392 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001393 }
1394 else
1395 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001396 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001397 {
Cristy71406342015-09-11 19:49:21 -04001398 SetPixelRed(image,ScaleShortToQuantum(
1399 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1400 SetPixelGreen(image,ScaleShortToQuantum(
1401 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1402 SetPixelBlue(image,ScaleShortToQuantum(
1403 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
cristy4c08aed2011-07-01 19:47:50 +00001404 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001405 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001406 }
1407 else
cristybb503372010-05-27 20:51:26 +00001408 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001409 {
Cristy71406342015-09-11 19:49:21 -04001410 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1411 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1412 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1413 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1414 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1415 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1416 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1417 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
cristy4c08aed2011-07-01 19:47:50 +00001418 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001419 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001420 }
1421 }
1422 else
1423 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001424 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001425 {
Cristy71406342015-09-11 19:49:21 -04001426 index=(Quantum) ConstrainColormapIndex(image,(ssize_t) GETJSAMPLE(*p),
1427 exception);
cristy4c08aed2011-07-01 19:47:50 +00001428 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001429 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001430 p++;
cristyed231572011-07-14 02:18:59 +00001431 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001432 }
1433 else
1434 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001435 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001436 {
cristy4c08aed2011-07-01 19:47:50 +00001437 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1438 GETJSAMPLE(*p++)),q);
1439 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1440 GETJSAMPLE(*p++)),q);
1441 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1442 GETJSAMPLE(*p++)),q);
1443 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001444 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001445 }
1446 else
cristybb503372010-05-27 20:51:26 +00001447 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001448 {
cristy4c08aed2011-07-01 19:47:50 +00001449 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1450 (unsigned char) GETJSAMPLE(*p++)),q);
1451 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1452 (unsigned char) GETJSAMPLE(*p++)),q);
1453 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1454 (unsigned char) GETJSAMPLE(*p++)),q);
1455 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1456 (unsigned char) GETJSAMPLE(*p++)),q);
1457 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001458 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001459 }
1460 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1461 break;
cristy524222d2011-04-25 00:37:06 +00001462 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1463 image->rows);
1464 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001465 {
1466 jpeg_abort_decompress(&jpeg_info);
1467 break;
1468 }
cristy3ed852e2009-09-05 21:47:34 +00001469 }
cristyd28b1dd2011-05-14 20:30:38 +00001470 if (status != MagickFalse)
1471 {
1472 error_manager.finished=MagickTrue;
1473 if (setjmp(error_manager.error_recovery) == 0)
1474 (void) jpeg_finish_decompress(&jpeg_info);
1475 }
cristy3ed852e2009-09-05 21:47:34 +00001476 /*
1477 Free jpeg resources.
1478 */
cristy3ed852e2009-09-05 21:47:34 +00001479 jpeg_destroy_decompress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00001480 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001481 (void) CloseBlob(image);
1482 return(GetFirstImageInList(image));
1483}
1484#endif
cristy86b6e552015-06-06 13:25:16 +00001485
cristy3ed852e2009-09-05 21:47:34 +00001486/*
1487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1488% %
1489% %
1490% %
1491% R e g i s t e r J P E G I m a g e %
1492% %
1493% %
1494% %
1495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1496%
1497% RegisterJPEGImage() adds properties for the JPEG image format to
1498% the list of supported formats. The properties include the image format
1499% tag, a method to read and/or write the format, whether the format
1500% supports the saving of more than one frame to the same file or blob,
1501% whether the format supports native in-memory I/O, and a brief
1502% description of the format.
1503%
1504% The format of the RegisterJPEGImage method is:
1505%
cristybb503372010-05-27 20:51:26 +00001506% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001507%
1508*/
cristybb503372010-05-27 20:51:26 +00001509ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001510{
dirk06b627a2015-04-06 18:59:17 +00001511#define JPEGDescription "Joint Photographic Experts Group JFIF format"
1512
cristy3ed852e2009-09-05 21:47:34 +00001513 char
cristy151b66d2015-04-15 10:50:31 +00001514 version[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001515
1516 MagickInfo
1517 *entry;
1518
cristy3ed852e2009-09-05 21:47:34 +00001519 *version='\0';
1520#if defined(JPEG_LIB_VERSION)
cristy151b66d2015-04-15 10:50:31 +00001521 (void) FormatLocaleString(version,MagickPathExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001522#endif
dirk06b627a2015-04-06 18:59:17 +00001523 entry=AcquireMagickInfo("JPEG","JPE",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001524#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001525 entry->flags^=CoderDecoderThreadSupportFlag;
cristyb338b712014-11-27 14:06:34 +00001526#endif
1527#if defined(MAGICKCORE_JPEG_DELEGATE)
1528 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1529 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1530#endif
1531 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001532 entry->flags^=CoderAdjoinFlag;
1533 entry->flags^=CoderUseExtensionFlag;
cristyb338b712014-11-27 14:06:34 +00001534 if (*version != '\0')
1535 entry->version=ConstantString(version);
1536 entry->mime_type=ConstantString("image/jpeg");
cristyb338b712014-11-27 14:06:34 +00001537 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001538 entry=AcquireMagickInfo("JPEG","JPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001539#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001540 entry->flags^=CoderDecoderThreadSupportFlag;
cristy61c382d2014-09-17 11:18:11 +00001541#endif
cristy3ed852e2009-09-05 21:47:34 +00001542#if defined(MAGICKCORE_JPEG_DELEGATE)
1543 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1544 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1545#endif
1546 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001547 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +00001548 if (*version != '\0')
1549 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001550 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001551 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001552 entry=AcquireMagickInfo("JPEG","JPG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001553#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001554 entry->flags^=CoderDecoderThreadSupportFlag;
cristy61c382d2014-09-17 11:18:11 +00001555#endif
cristy3ed852e2009-09-05 21:47:34 +00001556#if defined(MAGICKCORE_JPEG_DELEGATE)
1557 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1558 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1559#endif
dirk08e9a112015-02-22 01:51:41 +00001560 entry->flags^=CoderAdjoinFlag;
1561 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001562 if (*version != '\0')
1563 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001564 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001565 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001566 entry=AcquireMagickInfo("JPEG","JPS",JPEGDescription);
cristya663a4f2015-01-23 12:11:57 +00001567#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001568 entry->flags^=CoderDecoderThreadSupportFlag;
cristya663a4f2015-01-23 12:11:57 +00001569#endif
1570#if defined(MAGICKCORE_JPEG_DELEGATE)
1571 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1572 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1573#endif
dirk08e9a112015-02-22 01:51:41 +00001574 entry->flags^=CoderAdjoinFlag;
1575 entry->flags^=CoderUseExtensionFlag;
cristya663a4f2015-01-23 12:11:57 +00001576 if (*version != '\0')
1577 entry->version=ConstantString(version);
1578 entry->mime_type=ConstantString("image/jpeg");
cristya663a4f2015-01-23 12:11:57 +00001579 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001580 entry=AcquireMagickInfo("JPEG","PJPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001581#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
Cristy2b008a82015-09-07 09:51:07 -04001582 entry->flags^=CoderDecoderThreadSupportFlag;
cristy61c382d2014-09-17 11:18:11 +00001583#endif
cristy3ed852e2009-09-05 21:47:34 +00001584#if defined(MAGICKCORE_JPEG_DELEGATE)
1585 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1586 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1587#endif
dirk08e9a112015-02-22 01:51:41 +00001588 entry->flags^=CoderAdjoinFlag;
1589 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001590 if (*version != '\0')
1591 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001592 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001593 (void) RegisterMagickInfo(entry);
1594 return(MagickImageCoderSignature);
1595}
cristy86b6e552015-06-06 13:25:16 +00001596
cristy3ed852e2009-09-05 21:47:34 +00001597/*
1598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1599% %
1600% %
1601% %
1602% U n r e g i s t e r J P E G I m a g e %
1603% %
1604% %
1605% %
1606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1607%
1608% UnregisterJPEGImage() removes format registrations made by the
1609% JPEG module from the list of supported formats.
1610%
1611% The format of the UnregisterJPEGImage method is:
1612%
1613% UnregisterJPEGImage(void)
1614%
1615*/
1616ModuleExport void UnregisterJPEGImage(void)
1617{
1618 (void) UnregisterMagickInfo("PJPG");
cristya663a4f2015-01-23 12:11:57 +00001619 (void) UnregisterMagickInfo("JPS");
cristy3ed852e2009-09-05 21:47:34 +00001620 (void) UnregisterMagickInfo("JPG");
cristya663a4f2015-01-23 12:11:57 +00001621 (void) UnregisterMagickInfo("JPEG");
cristyb338b712014-11-27 14:06:34 +00001622 (void) UnregisterMagickInfo("JPE");
cristy3ed852e2009-09-05 21:47:34 +00001623}
cristy86b6e552015-06-06 13:25:16 +00001624
cristy3ed852e2009-09-05 21:47:34 +00001625#if defined(MAGICKCORE_JPEG_DELEGATE)
1626/*
1627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1628% %
1629% %
1630% %
1631% W r i t e J P E G I m a g e %
1632% %
1633% %
1634% %
1635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636%
1637% WriteJPEGImage() writes a JPEG image file and returns it. It
1638% allocates the memory necessary for the new Image structure and returns a
1639% pointer to the new image.
1640%
1641% The format of the WriteJPEGImage method is:
1642%
cristy91044972011-04-22 14:21:16 +00001643% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001644% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001645%
1646% A description of each parameter follows:
1647%
1648% o image_info: the image info.
1649%
1650% o jpeg_image: The image.
1651%
cristy1e178e72011-08-28 19:44:34 +00001652% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001653%
1654*/
1655
cristy1b58f252012-03-01 01:41:41 +00001656static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1657{
1658 assert(table != (QuantizationTable *) NULL);
1659 if (table->slot != (char *) NULL)
1660 table->slot=DestroyString(table->slot);
1661 if (table->description != (char *) NULL)
1662 table->description=DestroyString(table->description);
1663 if (table->levels != (unsigned int *) NULL)
1664 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1665 table=(QuantizationTable *) RelinquishMagickMemory(table);
1666 return(table);
1667}
1668
cristy3ed852e2009-09-05 21:47:34 +00001669static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1670{
1671 DestinationManager
1672 *destination;
1673
1674 destination=(DestinationManager *) cinfo->dest;
1675 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1676 MaxBufferExtent,destination->buffer);
1677 if (destination->manager.free_in_buffer != MaxBufferExtent)
1678 ERREXIT(cinfo,JERR_FILE_WRITE);
1679 destination->manager.next_output_byte=destination->buffer;
1680 return(TRUE);
1681}
1682
cristy1b58f252012-03-01 01:41:41 +00001683static QuantizationTable *GetQuantizationTable(const char *filename,
1684 const char *slot,ExceptionInfo *exception)
1685{
1686 char
1687 *p,
1688 *xml;
1689
1690 const char
1691 *attribute,
1692 *content;
1693
1694 double
1695 value;
1696
1697 register ssize_t
1698 i;
1699
1700 ssize_t
1701 j;
1702
1703 QuantizationTable
1704 *table;
1705
cristy1fcc8b62012-03-02 17:25:55 +00001706 size_t
1707 length;
1708
cristy1b58f252012-03-01 01:41:41 +00001709 XMLTreeInfo
1710 *description,
1711 *levels,
1712 *quantization_tables,
1713 *table_iterator;
1714
1715 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1716 "Loading quantization tables \"%s\" ...",filename);
1717 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001718 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001719 if (xml == (char *) NULL)
1720 return(table);
1721 quantization_tables=NewXMLTree(xml,exception);
1722 if (quantization_tables == (XMLTreeInfo *) NULL)
1723 {
1724 xml=DestroyString(xml);
1725 return(table);
1726 }
1727 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1728 table_iterator != (XMLTreeInfo *) NULL;
1729 table_iterator=GetNextXMLTreeTag(table_iterator))
1730 {
1731 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1732 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1733 break;
1734 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1735 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1736 break;
1737 }
1738 if (table_iterator == (XMLTreeInfo *) NULL)
1739 {
1740 xml=DestroyString(xml);
1741 return(table);
1742 }
1743 description=GetXMLTreeChild(table_iterator,"description");
1744 if (description == (XMLTreeInfo *) NULL)
1745 {
1746 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001747 "XmlMissingElement","<description>, slot \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001748 quantization_tables=DestroyXMLTree(quantization_tables);
1749 xml=DestroyString(xml);
1750 return(table);
1751 }
1752 levels=GetXMLTreeChild(table_iterator,"levels");
1753 if (levels == (XMLTreeInfo *) NULL)
1754 {
1755 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001756 "XmlMissingElement","<levels>, slot \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001757 quantization_tables=DestroyXMLTree(quantization_tables);
1758 xml=DestroyString(xml);
1759 return(table);
1760 }
1761 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1762 if (table == (QuantizationTable *) NULL)
1763 ThrowFatalException(ResourceLimitFatalError,
1764 "UnableToAcquireQuantizationTable");
1765 table->slot=(char *) NULL;
1766 table->description=(char *) NULL;
1767 table->levels=(unsigned int *) NULL;
1768 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1769 if (attribute != (char *) NULL)
1770 table->slot=ConstantString(attribute);
1771 content=GetXMLTreeContent(description);
1772 if (content != (char *) NULL)
1773 table->description=ConstantString(content);
1774 attribute=GetXMLTreeAttribute(levels,"width");
1775 if (attribute == (char *) NULL)
1776 {
1777 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001778 "XmlMissingAttribute","<levels width>, slot \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001779 quantization_tables=DestroyXMLTree(quantization_tables);
1780 table=DestroyQuantizationTable(table);
1781 xml=DestroyString(xml);
1782 return(table);
1783 }
1784 table->width=StringToUnsignedLong(attribute);
1785 if (table->width == 0)
1786 {
1787 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001788 "XmlInvalidAttribute","<levels width>, table \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001789 quantization_tables=DestroyXMLTree(quantization_tables);
1790 table=DestroyQuantizationTable(table);
1791 xml=DestroyString(xml);
1792 return(table);
1793 }
1794 attribute=GetXMLTreeAttribute(levels,"height");
1795 if (attribute == (char *) NULL)
1796 {
1797 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001798 "XmlMissingAttribute","<levels height>, table \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001799 quantization_tables=DestroyXMLTree(quantization_tables);
1800 table=DestroyQuantizationTable(table);
1801 xml=DestroyString(xml);
1802 return(table);
1803 }
1804 table->height=StringToUnsignedLong(attribute);
1805 if (table->height == 0)
1806 {
1807 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001808 "XmlInvalidAttribute","<levels height>, table \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001809 quantization_tables=DestroyXMLTree(quantization_tables);
1810 table=DestroyQuantizationTable(table);
1811 xml=DestroyString(xml);
1812 return(table);
1813 }
1814 attribute=GetXMLTreeAttribute(levels,"divisor");
1815 if (attribute == (char *) NULL)
1816 {
1817 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001818 "XmlMissingAttribute","<levels divisor>, table \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001819 quantization_tables=DestroyXMLTree(quantization_tables);
1820 table=DestroyQuantizationTable(table);
1821 xml=DestroyString(xml);
1822 return(table);
1823 }
cristy043f3f32012-03-02 17:37:28 +00001824 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1825 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001826 {
1827 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001828 "XmlInvalidAttribute","<levels divisor>, table \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001829 quantization_tables=DestroyXMLTree(quantization_tables);
1830 table=DestroyQuantizationTable(table);
1831 xml=DestroyString(xml);
1832 return(table);
1833 }
1834 content=GetXMLTreeContent(levels);
1835 if (content == (char *) NULL)
1836 {
1837 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001838 "XmlMissingContent","<levels>, table \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001839 quantization_tables=DestroyXMLTree(quantization_tables);
1840 table=DestroyQuantizationTable(table);
1841 xml=DestroyString(xml);
1842 return(table);
1843 }
cristy1fcc8b62012-03-02 17:25:55 +00001844 length=(size_t) table->width*table->height;
1845 if (length < 64)
1846 length=64;
cristy092006b2012-03-02 17:26:02 +00001847 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001848 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001849 if (table->levels == (unsigned int *) NULL)
1850 ThrowFatalException(ResourceLimitFatalError,
1851 "UnableToAcquireQuantizationTable");
1852 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1853 {
cristy043f3f32012-03-02 17:37:28 +00001854 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1855 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001856 while (isspace((int) ((unsigned char) *p)) != 0)
1857 p++;
1858 if (*p == ',')
1859 p++;
1860 content=p;
1861 }
cristy043f3f32012-03-02 17:37:28 +00001862 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001863 (void) value;
1864 if (p != content)
1865 {
1866 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
Cristy8f424002015-11-08 13:22:50 -05001867 "XmlInvalidContent","<level> too many values, table \"%s\"",slot);
cristy1b58f252012-03-01 01:41:41 +00001868 quantization_tables=DestroyXMLTree(quantization_tables);
1869 table=DestroyQuantizationTable(table);
1870 xml=DestroyString(xml);
1871 return(table);
1872 }
cristy043f3f32012-03-02 17:37:28 +00001873 for (j=i; j < 64; j++)
1874 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001875 quantization_tables=DestroyXMLTree(quantization_tables);
1876 xml=DestroyString(xml);
1877 return(table);
1878}
1879
cristy3ed852e2009-09-05 21:47:34 +00001880static void InitializeDestination(j_compress_ptr cinfo)
1881{
1882 DestinationManager
1883 *destination;
1884
1885 destination=(DestinationManager *) cinfo->dest;
1886 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1887 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1888 destination->manager.next_output_byte=destination->buffer;
1889 destination->manager.free_in_buffer=MaxBufferExtent;
1890}
1891
cristy3ed852e2009-09-05 21:47:34 +00001892static void TerminateDestination(j_compress_ptr cinfo)
1893{
1894 DestinationManager
1895 *destination;
1896
1897 destination=(DestinationManager *) cinfo->dest;
1898 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1899 {
1900 ssize_t
1901 count;
1902
1903 count=WriteBlob(destination->image,MaxBufferExtent-
1904 destination->manager.free_in_buffer,destination->buffer);
1905 if (count != (ssize_t)
1906 (MaxBufferExtent-destination->manager.free_in_buffer))
1907 ERREXIT(cinfo,JERR_FILE_WRITE);
1908 }
1909}
1910
1911static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1912{
1913 const char
1914 *name;
1915
1916 const StringInfo
1917 *profile;
1918
1919 MagickBooleanType
1920 iptc;
1921
cristybb503372010-05-27 20:51:26 +00001922 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001923 i;
1924
1925 size_t
cristy524222d2011-04-25 00:37:06 +00001926 length,
1927 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001928
1929 StringInfo
1930 *custom_profile;
1931
cristy3ed852e2009-09-05 21:47:34 +00001932 /*
1933 Save image profile as a APP marker.
1934 */
1935 iptc=MagickFalse;
1936 custom_profile=AcquireStringInfo(65535L);
1937 ResetImageProfileIterator(image);
1938 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1939 {
1940 profile=GetImageProfile(image,name);
1941 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001942 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001943 {
1944 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1945 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1946 (unsigned int) length);
1947 }
1948 if (LocaleCompare(name,"ICC") == 0)
1949 {
1950 register unsigned char
1951 *p;
1952
cristy8ba82ae2013-05-29 14:19:49 +00001953 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001954 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001955 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001956 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001957 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001958 {
1959 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001960 p[12]=(unsigned char) ((i/65519L)+1);
1961 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001962 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001963 length);
1964 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001965 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001966 }
1967 }
1968 if (((LocaleCompare(name,"IPTC") == 0) ||
1969 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1970 {
dirkc93932a2015-10-01 16:56:10 +02001971 register unsigned char
1972 *p;
1973
cristybb503372010-05-27 20:51:26 +00001974 size_t
cristy3ed852e2009-09-05 21:47:34 +00001975 roundup;
1976
1977 iptc=MagickTrue;
dirkc93932a2015-10-01 16:56:10 +02001978 p=GetStringInfoDatum(custom_profile);
cristybb503372010-05-27 20:51:26 +00001979 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001980 {
1981 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001982 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001983 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1984 {
1985 (void) memcpy(p,"Photoshop 3.0 ",14);
1986 tag_length=14;
1987 }
1988 else
1989 {
1990 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1991 tag_length=26;
1992 p[24]=(unsigned char) (length >> 8);
1993 p[25]=(unsigned char) (length & 0xff);
1994 }
1995 p[13]=0x00;
1996 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001997 if (roundup != 0)
1998 p[length+tag_length]='\0';
1999 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
2000 custom_profile),(unsigned int) (length+tag_length+roundup));
2001 }
2002 }
2003 if (LocaleCompare(name,"XMP") == 0)
2004 {
2005 StringInfo
2006 *xmp_profile;
2007
2008 /*
2009 Add namespace to XMP profile.
2010 */
cristy0615f0e2011-10-12 11:36:46 +00002011 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy30841e62014-05-19 00:45:15 +00002012 if (xmp_profile != (StringInfo *) NULL)
cristy8418c7e2014-05-18 18:38:26 +00002013 {
cristyc08a0aa2014-05-25 23:30:09 +00002014 if (profile != (StringInfo *) NULL)
2015 ConcatenateStringInfo(xmp_profile,profile);
cristy8418c7e2014-05-18 18:38:26 +00002016 GetStringInfoDatum(xmp_profile)[28]='\0';
2017 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
2018 {
2019 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
2020 jpeg_write_marker(jpeg_info,XML_MARKER,
2021 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
2022 }
2023 xmp_profile=DestroyStringInfo(xmp_profile);
2024 }
cristy3ed852e2009-09-05 21:47:34 +00002025 }
cristye8c25f92010-06-03 00:53:06 +00002026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2027 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00002028 name=GetNextImageProfile(image);
2029 }
2030 custom_profile=DestroyStringInfo(custom_profile);
2031}
2032
2033static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
2034{
2035 DestinationManager
2036 *destination;
2037
2038 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
2039 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
2040 destination=(DestinationManager *) cinfo->dest;
2041 destination->manager.init_destination=InitializeDestination;
2042 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2043 destination->manager.term_destination=TerminateDestination;
2044 destination->image=image;
2045}
2046
2047static char **SamplingFactorToList(const char *text)
2048{
2049 char
2050 **textlist;
2051
2052 register char
2053 *q;
2054
2055 register const char
2056 *p;
2057
cristybb503372010-05-27 20:51:26 +00002058 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002059 i;
2060
cristy3ed852e2009-09-05 21:47:34 +00002061 if (text == (char *) NULL)
2062 return((char **) NULL);
2063 /*
2064 Convert string to an ASCII list.
2065 */
Cristy71406342015-09-11 19:49:21 -04002066 textlist=(char **) AcquireQuantumMemory((size_t) MAX_COMPONENTS,
cristy3ed852e2009-09-05 21:47:34 +00002067 sizeof(*textlist));
2068 if (textlist == (char **) NULL)
2069 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2070 p=text;
Cristy71406342015-09-11 19:49:21 -04002071 for (i=0; i < (ssize_t) MAX_COMPONENTS; i++)
cristy3ed852e2009-09-05 21:47:34 +00002072 {
2073 for (q=(char *) p; *q != '\0'; q++)
2074 if (*q == ',')
2075 break;
cristy151b66d2015-04-15 10:50:31 +00002076 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00002077 sizeof(*textlist[i]));
2078 if (textlist[i] == (char *) NULL)
2079 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2080 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2081 if (*q == '\r')
2082 q++;
Cristy71406342015-09-11 19:49:21 -04002083 if (*q == '\0')
2084 break;
cristy3ed852e2009-09-05 21:47:34 +00002085 p=q+1;
2086 }
Cristy71406342015-09-11 19:49:21 -04002087 for (i++; i < (ssize_t) MAX_COMPONENTS; i++)
2088 textlist[i]=ConstantString("1x1");
cristy3ed852e2009-09-05 21:47:34 +00002089 return(textlist);
2090}
2091
2092static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002093 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002094{
2095 const char
2096 *option,
2097 *sampling_factor,
2098 *value;
2099
2100 ErrorManager
2101 error_manager;
2102
cristy86b6e552015-06-06 13:25:16 +00002103 Image
2104 *volatile volatile_image;
2105
cristy21749e92012-02-18 02:29:21 +00002106 int
cristy7b8ed292013-05-18 01:13:56 +00002107 colorspace,
cristy21749e92012-02-18 02:29:21 +00002108 quality;
2109
cristy3ed852e2009-09-05 21:47:34 +00002110 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002111 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002112
2113 JSAMPROW
2114 scanline[1];
2115
cristy3ed852e2009-09-05 21:47:34 +00002116 MagickBooleanType
2117 status;
2118
cristy22646e22013-06-23 16:34:03 +00002119 MemoryInfo
2120 *memory_info;
2121
cristy3ed852e2009-09-05 21:47:34 +00002122 register JSAMPLE
2123 *q;
2124
cristybb503372010-05-27 20:51:26 +00002125 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002126 i;
2127
cristy524222d2011-04-25 00:37:06 +00002128 ssize_t
2129 y;
2130
cristy3ed852e2009-09-05 21:47:34 +00002131 struct jpeg_compress_struct
2132 jpeg_info;
2133
2134 struct jpeg_error_mgr
2135 jpeg_error;
2136
cristy5328a4c2013-12-03 11:32:13 +00002137 unsigned short
2138 scale;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Open image file.
2142 */
2143 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002144 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002145 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002146 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002147 if (image->debug != MagickFalse)
2148 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002149 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002150 assert(exception->signature == MagickCoreSignature);
cristya663a4f2015-01-23 12:11:57 +00002151 if ((LocaleCompare(image_info->magick,"JPS") == 0) &&
2152 (image->next != (Image *) NULL))
2153 image=AppendImages(image,MagickFalse,exception);
cristy73ef4eb2015-01-23 14:31:57 +00002154 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2155 if (status == MagickFalse)
2156 return(status);
cristy3ed852e2009-09-05 21:47:34 +00002157 /*
2158 Initialize JPEG parameters.
2159 */
cristy91044972011-04-22 14:21:16 +00002160 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002161 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2162 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
cristy86b6e552015-06-06 13:25:16 +00002163 volatile_image=image;
2164 jpeg_info.client_data=(void *) volatile_image;
cristy3ed852e2009-09-05 21:47:34 +00002165 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002166 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002167 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002168 error_manager.exception=exception;
cristy86b6e552015-06-06 13:25:16 +00002169 error_manager.image=volatile_image;
cristy72f87f82013-06-23 16:43:05 +00002170 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002171 if (setjmp(error_manager.error_recovery) != 0)
2172 {
2173 jpeg_destroy_compress(&jpeg_info);
cristy86b6e552015-06-06 13:25:16 +00002174 (void) CloseBlob(volatile_image);
cristy3ed852e2009-09-05 21:47:34 +00002175 return(MagickFalse);
2176 }
2177 jpeg_info.client_data=(void *) &error_manager;
2178 jpeg_create_compress(&jpeg_info);
2179 JPEGDestinationManager(&jpeg_info,image);
2180 if ((image->columns != (unsigned int) image->columns) ||
2181 (image->rows != (unsigned int) image->rows))
2182 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2183 jpeg_info.image_width=(unsigned int) image->columns;
2184 jpeg_info.image_height=(unsigned int) image->rows;
2185 jpeg_info.input_components=3;
2186 jpeg_info.data_precision=8;
2187 jpeg_info.in_color_space=JCS_RGB;
2188 switch (image->colorspace)
2189 {
2190 case CMYKColorspace:
2191 {
2192 jpeg_info.input_components=4;
2193 jpeg_info.in_color_space=JCS_CMYK;
2194 break;
2195 }
2196 case YCbCrColorspace:
2197 case Rec601YCbCrColorspace:
2198 case Rec709YCbCrColorspace:
2199 {
2200 jpeg_info.in_color_space=JCS_YCbCr;
2201 break;
2202 }
2203 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002204 {
cristye9355552014-01-16 13:35:05 +00002205 if (image_info->type == TrueColorType)
2206 break;
cristy3ed852e2009-09-05 21:47:34 +00002207 jpeg_info.input_components=1;
2208 jpeg_info.in_color_space=JCS_GRAYSCALE;
2209 break;
2210 }
2211 default:
cristy9f396782009-12-21 01:37:45 +00002212 {
cristyaf8d3912014-02-21 14:50:33 +00002213 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye9355552014-01-16 13:35:05 +00002214 if (image_info->type == TrueColorType)
2215 break;
dirkf1d85482015-04-06 00:36:00 +00002216 if (SetImageGray(image,exception) != MagickFalse)
cristye9355552014-01-16 13:35:05 +00002217 {
2218 jpeg_info.input_components=1;
2219 jpeg_info.in_color_space=JCS_GRAYSCALE;
2220 }
cristy3ed852e2009-09-05 21:47:34 +00002221 break;
cristy9f396782009-12-21 01:37:45 +00002222 }
cristy3ed852e2009-09-05 21:47:34 +00002223 }
cristy3ed852e2009-09-05 21:47:34 +00002224 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002225 if (jpeg_info.in_color_space == JCS_CMYK)
2226 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002227 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2228 jpeg_info.data_precision=8;
2229 else
dirk93b02b72013-11-16 16:03:36 +00002230 jpeg_info.data_precision=BITS_IN_JSAMPLE;
cristy3ed852e2009-09-05 21:47:34 +00002231 if (image->debug != MagickFalse)
2232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
Cristy39b28d32015-09-29 08:20:12 -04002233 "Image resolution: %.20g,%.20g",image->resolution.x,image->resolution.y);
cristy2a11bef2011-10-28 18:33:11 +00002234 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002235 {
2236 /*
2237 Set image resolution.
2238 */
cristydf878012014-01-12 23:39:40 +00002239 jpeg_info.write_JFIF_header=TRUE;
Cristy39b28d32015-09-29 08:20:12 -04002240 jpeg_info.X_density=(UINT16) image->resolution.x;
2241 jpeg_info.Y_density=(UINT16) image->resolution.y;
cristy0c220632013-09-25 22:10:28 +00002242 /*
2243 Set image resolution units.
2244 */
cristy3ed852e2009-09-05 21:47:34 +00002245 if (image->units == PixelsPerInchResolution)
2246 jpeg_info.density_unit=(UINT8) 1;
2247 if (image->units == PixelsPerCentimeterResolution)
2248 jpeg_info.density_unit=(UINT8) 2;
2249 }
cristy97cb3bf2012-02-21 18:31:20 +00002250 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002251 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002252 if (option != (const char *) NULL)
2253 switch (*option)
2254 {
2255 case 'D':
2256 case 'd':
2257 {
2258 if (LocaleCompare(option,"default") == 0)
2259 jpeg_info.dct_method=JDCT_DEFAULT;
2260 break;
2261 }
2262 case 'F':
2263 case 'f':
2264 {
2265 if (LocaleCompare(option,"fastest") == 0)
2266 jpeg_info.dct_method=JDCT_FASTEST;
2267 if (LocaleCompare(option,"float") == 0)
2268 jpeg_info.dct_method=JDCT_FLOAT;
2269 break;
2270 }
2271 case 'I':
2272 case 'i':
2273 {
2274 if (LocaleCompare(option,"ifast") == 0)
2275 jpeg_info.dct_method=JDCT_IFAST;
2276 if (LocaleCompare(option,"islow") == 0)
2277 jpeg_info.dct_method=JDCT_ISLOW;
2278 break;
2279 }
2280 }
cristy092ec8d2013-04-26 13:46:22 +00002281 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002282 if (option != (const char *) NULL)
cristyb9d0e7e2014-11-02 18:14:44 +00002283 jpeg_info.optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE :
2284 FALSE;
cristy7d31f6e2014-11-11 13:07:58 +00002285 else
2286 {
2287 MagickSizeType
2288 length;
2289
2290 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2291 image->rows*sizeof(JSAMPLE);
2292 if (length == (MagickSizeType) ((size_t) length))
2293 {
2294 /*
2295 Perform optimization only if available memory resources permit it.
2296 */
2297 status=AcquireMagickResource(MemoryResource,length);
2298 RelinquishMagickResource(MemoryResource,length);
2299 jpeg_info.optimize_coding=status == MagickFalse ? FALSE : TRUE;
2300 }
2301 }
cristy3ed852e2009-09-05 21:47:34 +00002302#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2303 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2304 (image_info->interlace != NoInterlace))
2305 {
2306 if (image->debug != MagickFalse)
2307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2308 "Interlace: progressive");
2309 jpeg_simple_progression(&jpeg_info);
2310 }
2311 else
2312 if (image->debug != MagickFalse)
2313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2314 "Interlace: non-progressive");
2315#else
2316 if (image->debug != MagickFalse)
2317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2318 "Interlace: nonprogressive");
2319#endif
dirk871abab2013-11-03 13:21:16 +00002320 quality=92;
cristy2a950402014-03-12 17:57:14 +00002321 if ((image_info->compression != LosslessJPEGCompression) &&
2322 (image->quality <= 100))
2323 {
2324 if (image->quality != UndefinedCompressionQuality)
2325 quality=(int) image->quality;
2326 if (image->debug != MagickFalse)
2327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2328 (double) image->quality);
2329 }
2330 else
2331 {
2332#if !defined(C_LOSSLESS_SUPPORTED)
2333 quality=100;
2334 if (image->debug != MagickFalse)
2335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2336#else
2337 if (image->quality < 100)
2338 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2339 "LosslessToLossyJPEGConversion",image->filename);
2340 else
2341 {
2342 int
2343 point_transform,
2344 predictor;
2345
2346 predictor=image->quality/100; /* range 1-7 */
2347 point_transform=image->quality % 20; /* range 0-15 */
2348 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2349 if (image->debug != MagickFalse)
2350 {
2351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2352 "Compression: lossless");
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2354 "Predictor: %d",predictor);
2355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2356 "Point Transform: %d",point_transform);
2357 }
2358 }
2359#endif
2360 }
cristy092ec8d2013-04-26 13:46:22 +00002361 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002362 if (option != (const char *) NULL)
2363 {
2364 Image
2365 *jpeg_image;
2366
2367 ImageInfo
dirkc93932a2015-10-01 16:56:10 +02002368 *extent_info;
cristy0adb4f92009-11-28 18:08:51 +00002369
dirkc93932a2015-10-01 16:56:10 +02002370 extent_info=CloneImageInfo(image_info);
2371 extent_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002372 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002373 if (jpeg_image != (Image *) NULL)
2374 {
2375 MagickSizeType
2376 extent;
2377
2378 size_t
cristy87e73ab2010-02-12 01:59:12 +00002379 maximum,
2380 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002381
2382 /*
2383 Search for compression quality that does not exceed image extent.
2384 */
dirkc93932a2015-10-01 16:56:10 +02002385 extent_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002386 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
dirkc93932a2015-10-01 16:56:10 +02002387 (void) DeleteImageOption(extent_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002388 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
dirkc09781a2015-10-16 21:27:20 +02002389 maximum=image_info->quality;
Cristyab6e6092015-10-16 15:35:23 -04002390 if (maximum < 2)
dirkc09781a2015-10-16 21:27:20 +02002391 maximum=101;
dirkcf845242013-08-11 21:36:19 +00002392 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002393 {
cristy3975f402014-02-26 14:36:33 +00002394 (void) AcquireUniqueFilename(jpeg_image->filename);
2395 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
dirkc93932a2015-10-01 16:56:10 +02002396 status=WriteJPEGImage(extent_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002397 if (GetBlobSize(jpeg_image) <= extent)
cristy3975f402014-02-26 14:36:33 +00002398 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002399 else
cristy3975f402014-02-26 14:36:33 +00002400 maximum=jpeg_image->quality-1;
2401 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristy0adb4f92009-11-28 18:08:51 +00002402 }
cristy58d4fe12015-02-07 11:53:05 +00002403 quality=(int) minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002404 jpeg_image=DestroyImage(jpeg_image);
2405 }
dirkc93932a2015-10-01 16:56:10 +02002406 extent_info=DestroyImageInfo(extent_info);
cristy0adb4f92009-11-28 18:08:51 +00002407 }
cristy9bb7c842014-06-17 23:42:24 +00002408 jpeg_set_quality(&jpeg_info,quality,TRUE);
cristy16e74722012-07-01 13:01:38 +00002409#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002410 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002411 if (option != (const char *) NULL)
2412 {
2413 GeometryInfo
2414 geometry_info;
2415
2416 int
2417 flags;
2418
2419 /*
2420 Set quality scaling for luminance and chrominance separately.
2421 */
2422 flags=ParseGeometry(option,&geometry_info);
2423 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2424 {
2425 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2426 (geometry_info.rho+0.5));
2427 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2428 (geometry_info.sigma+0.5));
cristy9bb7c842014-06-17 23:42:24 +00002429 jpeg_default_qtables(&jpeg_info,TRUE);
cristy16e74722012-07-01 13:01:38 +00002430 }
2431 }
2432#endif
cristy7b8ed292013-05-18 01:13:56 +00002433 colorspace=jpeg_info.in_color_space;
2434 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002435 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002436 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002437 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002438 colorspace=StringToInteger(value);
2439 sampling_factor=(const char *) NULL;
2440 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002441 {
cristy7b8ed292013-05-18 01:13:56 +00002442 value=GetImageOption(image_info,"jpeg:sampling-factor");
2443 if (value == (char *) NULL)
2444 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2445 if (value != (char *) NULL)
2446 {
2447 sampling_factor=value;
2448 if (image->debug != MagickFalse)
2449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2450 " Input sampling-factors=%s",sampling_factor);
2451 }
cristy3ed852e2009-09-05 21:47:34 +00002452 }
cristy7b8ed292013-05-18 01:13:56 +00002453 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002454 if (image_info->sampling_factor != (char *) NULL)
2455 sampling_factor=image_info->sampling_factor;
2456 if (sampling_factor == (const char *) NULL)
2457 {
cristyc243f622015-02-21 17:20:34 +00002458 if (quality >= 90)
cristy3ed852e2009-09-05 21:47:34 +00002459 for (i=0; i < MAX_COMPONENTS; i++)
2460 {
2461 jpeg_info.comp_info[i].h_samp_factor=1;
2462 jpeg_info.comp_info[i].v_samp_factor=1;
2463 }
2464 }
2465 else
2466 {
2467 char
2468 **factors;
2469
2470 GeometryInfo
2471 geometry_info;
2472
2473 MagickStatusType
2474 flags;
2475
2476 /*
2477 Set sampling factor.
2478 */
2479 i=0;
2480 factors=SamplingFactorToList(sampling_factor);
2481 if (factors != (char **) NULL)
2482 {
2483 for (i=0; i < MAX_COMPONENTS; i++)
2484 {
2485 if (factors[i] == (char *) NULL)
2486 break;
2487 flags=ParseGeometry(factors[i],&geometry_info);
2488 if ((flags & SigmaValue) == 0)
2489 geometry_info.sigma=geometry_info.rho;
2490 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2491 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2492 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2493 }
2494 factors=(char **) RelinquishMagickMemory(factors);
2495 }
2496 for ( ; i < MAX_COMPONENTS; i++)
2497 {
2498 jpeg_info.comp_info[i].h_samp_factor=1;
2499 jpeg_info.comp_info[i].v_samp_factor=1;
2500 }
2501 }
cristy092ec8d2013-04-26 13:46:22 +00002502 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002503 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002504 {
cristy1b58f252012-03-01 01:41:41 +00002505 QuantizationTable
2506 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002507
cristy1445b322012-02-19 18:57:30 +00002508 /*
cristy1b58f252012-03-01 01:41:41 +00002509 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002510 */
cristy1b58f252012-03-01 01:41:41 +00002511 table=GetQuantizationTable(option,"0",exception);
2512 if (table != (QuantizationTable *) NULL)
2513 {
cristyae453b72013-04-21 22:24:20 +00002514 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002515 jpeg_info.comp_info[i].quant_tbl_no=0;
2516 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2517 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002518 table=DestroyQuantizationTable(table);
2519 }
2520 table=GetQuantizationTable(option,"1",exception);
2521 if (table != (QuantizationTable *) NULL)
2522 {
cristyae453b72013-04-21 22:24:20 +00002523 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002524 jpeg_info.comp_info[i].quant_tbl_no=1;
2525 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2526 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002527 table=DestroyQuantizationTable(table);
2528 }
2529 table=GetQuantizationTable(option,"2",exception);
2530 if (table != (QuantizationTable *) NULL)
2531 {
cristyae453b72013-04-21 22:24:20 +00002532 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002533 jpeg_info.comp_info[i].quant_tbl_no=2;
2534 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2535 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002536 table=DestroyQuantizationTable(table);
2537 }
2538 table=GetQuantizationTable(option,"3",exception);
2539 if (table != (QuantizationTable *) NULL)
2540 {
cristyae453b72013-04-21 22:24:20 +00002541 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002542 jpeg_info.comp_info[i].quant_tbl_no=3;
2543 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2544 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002545 table=DestroyQuantizationTable(table);
2546 }
cristy1445b322012-02-19 18:57:30 +00002547 }
cristy9bb7c842014-06-17 23:42:24 +00002548 jpeg_start_compress(&jpeg_info,TRUE);
cristy3ed852e2009-09-05 21:47:34 +00002549 if (image->debug != MagickFalse)
2550 {
2551 if (image->storage_class == PseudoClass)
2552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2553 "Storage class: PseudoClass");
2554 else
2555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2556 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2558 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002559 if (image->colors != 0)
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002561 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002562 else
2563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2564 "Number of colors: unspecified");
2565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2566 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2567 switch (image->colorspace)
2568 {
2569 case CMYKColorspace:
2570 {
2571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2572 "Storage class: DirectClass");
2573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2574 "Colorspace: CMYK");
2575 break;
2576 }
2577 case YCbCrColorspace:
2578 case Rec601YCbCrColorspace:
2579 case Rec709YCbCrColorspace:
2580 {
2581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 "Colorspace: YCbCr");
2583 break;
2584 }
2585 default:
2586 break;
2587 }
2588 switch (image->colorspace)
2589 {
2590 case CMYKColorspace:
2591 {
2592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593 "Colorspace: CMYK");
2594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2595 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2596 jpeg_info.comp_info[0].h_samp_factor,
2597 jpeg_info.comp_info[0].v_samp_factor,
2598 jpeg_info.comp_info[1].h_samp_factor,
2599 jpeg_info.comp_info[1].v_samp_factor,
2600 jpeg_info.comp_info[2].h_samp_factor,
2601 jpeg_info.comp_info[2].v_samp_factor,
2602 jpeg_info.comp_info[3].h_samp_factor,
2603 jpeg_info.comp_info[3].v_samp_factor);
2604 break;
2605 }
2606 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002607 {
2608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2609 "Colorspace: GRAY");
2610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2611 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2612 jpeg_info.comp_info[0].v_samp_factor);
2613 break;
2614 }
cristyed8d7852013-08-01 22:44:22 +00002615 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002616 case RGBColorspace:
2617 {
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002619 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2621 "Sampling factors: %dx%d,%dx%d,%dx%d",
2622 jpeg_info.comp_info[0].h_samp_factor,
2623 jpeg_info.comp_info[0].v_samp_factor,
2624 jpeg_info.comp_info[1].h_samp_factor,
2625 jpeg_info.comp_info[1].v_samp_factor,
2626 jpeg_info.comp_info[2].h_samp_factor,
2627 jpeg_info.comp_info[2].v_samp_factor);
2628 break;
2629 }
2630 case YCbCrColorspace:
2631 case Rec601YCbCrColorspace:
2632 case Rec709YCbCrColorspace:
2633 {
2634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2635 "Colorspace: YCbCr");
2636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2637 "Sampling factors: %dx%d,%dx%d,%dx%d",
2638 jpeg_info.comp_info[0].h_samp_factor,
2639 jpeg_info.comp_info[0].v_samp_factor,
2640 jpeg_info.comp_info[1].h_samp_factor,
2641 jpeg_info.comp_info[1].v_samp_factor,
2642 jpeg_info.comp_info[2].h_samp_factor,
2643 jpeg_info.comp_info[2].v_samp_factor);
2644 break;
2645 }
2646 default:
2647 {
2648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2649 image->colorspace);
2650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2651 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2652 jpeg_info.comp_info[0].h_samp_factor,
2653 jpeg_info.comp_info[0].v_samp_factor,
2654 jpeg_info.comp_info[1].h_samp_factor,
2655 jpeg_info.comp_info[1].v_samp_factor,
2656 jpeg_info.comp_info[2].h_samp_factor,
2657 jpeg_info.comp_info[2].v_samp_factor,
2658 jpeg_info.comp_info[3].h_samp_factor,
2659 jpeg_info.comp_info[3].v_samp_factor);
2660 break;
2661 }
2662 }
2663 }
2664 /*
2665 Write JPEG profiles.
2666 */
cristyd15e6592011-10-15 00:13:06 +00002667 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002668 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002669 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002670 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2671 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2672 if (image->profiles != (void *) NULL)
2673 WriteProfile(&jpeg_info,image);
2674 /*
2675 Convert MIFF to JPEG raster pixels.
2676 */
cristy22646e22013-06-23 16:34:03 +00002677 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002678 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002679 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002680 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002681 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002682 if (setjmp(error_manager.error_recovery) != 0)
2683 {
2684 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002685 if (memory_info != (MemoryInfo *) NULL)
2686 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002687 (void) CloseBlob(image);
2688 return(MagickFalse);
2689 }
2690 scanline[0]=(JSAMPROW) jpeg_pixels;
Cristy71406342015-09-11 19:49:21 -04002691 scale=65535/(unsigned short) GetQuantumRange((size_t)
2692 jpeg_info.data_precision);
cristy31db6c12013-12-04 15:17:30 +00002693 if (scale == 0)
Cristy71406342015-09-11 19:49:21 -04002694 scale=1;
cristye90d7402010-03-14 18:21:29 +00002695 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002696 {
cristy3ed852e2009-09-05 21:47:34 +00002697 if ((jpeg_info.in_color_space == JCS_RGB) ||
2698 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002699 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002700 {
cristy4c08aed2011-07-01 19:47:50 +00002701 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002702 *p;
2703
cristybb503372010-05-27 20:51:26 +00002704 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002705 x;
2706
cristy1e178e72011-08-28 19:44:34 +00002707 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002708 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002709 break;
2710 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002711 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002712 {
cristy4c08aed2011-07-01 19:47:50 +00002713 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2714 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2715 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002716 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002717 }
2718 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002719 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2720 image->rows);
2721 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002722 break;
2723 }
2724 else
cristye90d7402010-03-14 18:21:29 +00002725 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002726 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002727 {
cristyd0323222013-04-07 16:13:21 +00002728 register const Quantum
2729 *p;
2730
2731 register ssize_t
2732 x;
2733
2734 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2735 if (p == (const Quantum *) NULL)
2736 break;
2737 q=jpeg_pixels;
2738 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002739 {
cristyd0323222013-04-07 16:13:21 +00002740 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2741 image,p)));
2742 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002743 }
cristyd0323222013-04-07 16:13:21 +00002744 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2745 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2746 image->rows);
2747 if (status == MagickFalse)
2748 break;
2749 }
cristye90d7402010-03-14 18:21:29 +00002750 else
cristybb503372010-05-27 20:51:26 +00002751 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002752 {
cristy4c08aed2011-07-01 19:47:50 +00002753 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002754 *p;
2755
cristybb503372010-05-27 20:51:26 +00002756 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002757 x;
2758
cristy1e178e72011-08-28 19:44:34 +00002759 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002760 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002761 break;
2762 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002763 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002764 {
2765 /*
2766 Convert DirectClass packets to contiguous CMYK scanlines.
2767 */
2768 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002769 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002770 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002771 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002772 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002773 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002774 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002775 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002776 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002777 }
2778 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002779 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2780 image->rows);
2781 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002782 break;
2783 }
2784 }
2785 else
2786 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002787 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002788 {
cristy4c08aed2011-07-01 19:47:50 +00002789 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002790 *p;
2791
cristybb503372010-05-27 20:51:26 +00002792 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002793 x;
2794
cristy1e178e72011-08-28 19:44:34 +00002795 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002796 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002797 break;
2798 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002799 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002800 {
cristy217ddd62013-12-06 23:27:09 +00002801 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2802 p)))/scale);
cristyed231572011-07-14 02:18:59 +00002803 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002804 }
2805 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002806 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2807 image->rows);
2808 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002809 break;
2810 }
2811 else
2812 if ((jpeg_info.in_color_space == JCS_RGB) ||
2813 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002814 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002815 {
cristy4c08aed2011-07-01 19:47:50 +00002816 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002817 *p;
2818
cristybb503372010-05-27 20:51:26 +00002819 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002820 x;
2821
cristy1e178e72011-08-28 19:44:34 +00002822 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002823 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002824 break;
2825 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002826 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002827 {
cristy5328a4c2013-12-03 11:32:13 +00002828 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2829 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2830 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002831 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002832 }
2833 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002834 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002835 image->rows);
cristye90d7402010-03-14 18:21:29 +00002836 if (status == MagickFalse)
2837 break;
2838 }
2839 else
cristybb503372010-05-27 20:51:26 +00002840 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002841 {
cristy4c08aed2011-07-01 19:47:50 +00002842 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002843 *p;
2844
cristybb503372010-05-27 20:51:26 +00002845 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002846 x;
2847
cristy1e178e72011-08-28 19:44:34 +00002848 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002849 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002850 break;
2851 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002852 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002853 {
2854 /*
2855 Convert DirectClass packets to contiguous CMYK scanlines.
2856 */
cristy217ddd62013-12-06 23:27:09 +00002857 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2858 image,p))/scale);
2859 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2860 image,p))/scale);
2861 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2862 image,p))/scale);
2863 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2864 image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002865 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002866 }
2867 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002868 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002869 image->rows);
cristye90d7402010-03-14 18:21:29 +00002870 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002871 break;
2872 }
cristybb503372010-05-27 20:51:26 +00002873 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002874 jpeg_finish_compress(&jpeg_info);
2875 /*
2876 Relinquish resources.
2877 */
2878 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002879 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002880 (void) CloseBlob(image);
2881 return(MagickTrue);
2882}
2883#endif