blob: 7f4e0fb373920f74d92cbd484ac69714b3ae1f9d [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
cristy05c0c9a2011-09-05 23:16:13 +0000726 length;
727
728 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),
733 length);
cristy08f255a2011-08-17 01:23:06 +0000734 (void) memcpy(GetStringInfoDatum(profile),
735 GetStringInfoDatum(previous_profile),
736 GetStringInfoLength(previous_profile));
737 }
cristyd15e6592011-10-15 00:13:06 +0000738 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000739 profile=DestroyStringInfo(profile);
740 if (status == MagickFalse)
cristydf878012014-01-12 23:39:40 +0000741 {
742 (void) ThrowMagickException(exception,GetMagickModule(),
743 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
744 return(FALSE);
745 }
cristy3ed852e2009-09-05 21:47:34 +0000746 if (image->debug != MagickFalse)
747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000748 "Profile: %s, %.20g bytes",name,(double) length);
cristydf878012014-01-12 23:39:40 +0000749 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000750}
751
cristyf2faecf2010-05-28 19:19:36 +0000752static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000753{
754 SourceManager
755 *source;
756
757 if (number_bytes <= 0)
758 return;
759 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000760 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000761 {
cristyf2faecf2010-05-28 19:19:36 +0000762 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000763 (void) FillInputBuffer(cinfo);
764 }
cristy4cb162a2010-05-30 03:04:47 +0000765 source->manager.next_input_byte+=number_bytes;
766 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000767}
768
769static void TerminateSource(j_decompress_ptr cinfo)
770{
cristydf0d90e2011-12-12 01:03:55 +0000771 (void) cinfo;
cristy3ed852e2009-09-05 21:47:34 +0000772}
773
774static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
775{
776 SourceManager
777 *source;
778
779 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
780 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
781 source=(SourceManager *) cinfo->src;
782 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
783 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
784 source=(SourceManager *) cinfo->src;
785 source->manager.init_source=InitializeSource;
786 source->manager.fill_input_buffer=FillInputBuffer;
787 source->manager.skip_input_data=SkipInputData;
788 source->manager.resync_to_restart=jpeg_resync_to_restart;
789 source->manager.term_source=TerminateSource;
790 source->manager.bytes_in_buffer=0;
791 source->manager.next_input_byte=NULL;
792 source->image=image;
793}
794
795static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
dirk881355a2013-08-19 19:56:50 +0000796 Image *image, ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000797{
798 image->quality=UndefinedCompressionQuality;
799#if defined(D_PROGRESSIVE_SUPPORTED)
800 if (image->compression == LosslessJPEGCompression)
801 {
dirk29dd80e2013-10-31 23:11:11 +0000802 image->quality=100;
cristy3ed852e2009-09-05 21:47:34 +0000803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
804 "Quality: 100 (lossless)");
805 }
806 else
807#endif
808 {
cristybb503372010-05-27 20:51:26 +0000809 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000810 j,
811 qvalue,
812 sum;
813
cristybb503372010-05-27 20:51:26 +0000814 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000815 i;
816
817 /*
818 Determine the JPEG compression quality from the quantization tables.
819 */
820 sum=0;
821 for (i=0; i < NUM_QUANT_TBLS; i++)
822 {
823 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
824 for (j=0; j < DCTSIZE2; j++)
825 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
dirk881355a2013-08-19 19:56:50 +0000826 }
827 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
828 (jpeg_info->quant_tbl_ptrs[1] != NULL))
829 {
830 ssize_t
831 hash[101] =
832 {
833 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
834 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
835 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
836 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
837 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
838 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
839 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
840 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
841 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
842 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
843 0
844 },
845 sums[101] =
846 {
847 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
848 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
849 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
850 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
851 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
852 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
853 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
854 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
855 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
856 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
857 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
858 128, 0
859 };
860
861 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
862 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
863 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
864 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
865 for (i=0; i < 100; i++)
866 {
867 if ((qvalue < hash[i]) && (sum < sums[i]))
868 continue;
869 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000870 image->quality=(size_t) i+1;
dirk881355a2013-08-19 19:56:50 +0000871 if (image->debug != MagickFalse)
872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
873 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
874 (sum <= sums[i]) ? "exact" : "approximate");
875 break;
876 }
877 }
878 else
879 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
880 {
881 ssize_t
882 hash[101] =
883 {
884 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
885 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
886 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
887 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
888 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
889 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
890 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
891 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
892 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
893 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
cristy3ed852e2009-09-05 21:47:34 +0000894 0
dirk881355a2013-08-19 19:56:50 +0000895 },
896 sums[101] =
897 {
898 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
899 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
900 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
901 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
902 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
903 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
904 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
905 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
906 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
907 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
908 667, 592, 518, 441, 369, 292, 221, 151, 86,
909 64, 0
910 };
cristy3ed852e2009-09-05 21:47:34 +0000911
dirk881355a2013-08-19 19:56:50 +0000912 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
913 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
914 for (i=0; i < 100; i++)
915 {
916 if ((qvalue < hash[i]) && (sum < sums[i]))
917 continue;
918 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000919 image->quality=(size_t)i+1;
dirk881355a2013-08-19 19:56:50 +0000920 if (image->debug != MagickFalse)
921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
922 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
923 (sum <= sums[i]) ? "exact" : "approximate");
924 break;
925 }
926 }
cristy3ed852e2009-09-05 21:47:34 +0000927 }
928}
929
cristyd15e6592011-10-15 00:13:06 +0000930static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000931{
932 char
cristy151b66d2015-04-15 10:50:31 +0000933 sampling_factor[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000934
935 switch (jpeg_info->out_color_space)
936 {
937 case JCS_CMYK:
938 {
939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristy151b66d2015-04-15 10:50:31 +0000940 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000941 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
942 jpeg_info->comp_info[0].v_samp_factor,
943 jpeg_info->comp_info[1].h_samp_factor,
944 jpeg_info->comp_info[1].v_samp_factor,
945 jpeg_info->comp_info[2].h_samp_factor,
946 jpeg_info->comp_info[2].v_samp_factor,
947 jpeg_info->comp_info[3].h_samp_factor,
948 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000949 break;
cristy3ed852e2009-09-05 21:47:34 +0000950 }
951 case JCS_GRAYSCALE:
952 {
953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
954 "Colorspace: GRAYSCALE");
cristy151b66d2015-04-15 10:50:31 +0000955 (void) FormatLocaleString(sampling_factor,MagickPathExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000956 jpeg_info->comp_info[0].h_samp_factor,
957 jpeg_info->comp_info[0].v_samp_factor);
958 break;
959 }
960 case JCS_RGB:
961 {
962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristy151b66d2015-04-15 10:50:31 +0000963 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000964 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
965 jpeg_info->comp_info[0].v_samp_factor,
966 jpeg_info->comp_info[1].h_samp_factor,
967 jpeg_info->comp_info[1].v_samp_factor,
968 jpeg_info->comp_info[2].h_samp_factor,
969 jpeg_info->comp_info[2].v_samp_factor);
970 break;
971 }
972 default:
973 {
974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
975 jpeg_info->out_color_space);
cristy151b66d2015-04-15 10:50:31 +0000976 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000977 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
978 jpeg_info->comp_info[0].v_samp_factor,
979 jpeg_info->comp_info[1].h_samp_factor,
980 jpeg_info->comp_info[1].v_samp_factor,
981 jpeg_info->comp_info[2].h_samp_factor,
982 jpeg_info->comp_info[2].v_samp_factor,
983 jpeg_info->comp_info[3].h_samp_factor,
984 jpeg_info->comp_info[3].v_samp_factor);
985 break;
986 }
987 }
cristyd15e6592011-10-15 00:13:06 +0000988 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
989 exception);
cristye90d7402010-03-14 18:21:29 +0000990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
991 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000992}
993
994static Image *ReadJPEGImage(const ImageInfo *image_info,
995 ExceptionInfo *exception)
996{
997 char
cristy151b66d2015-04-15 10:50:31 +0000998 value[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000999
cristycaec74e2009-09-14 02:20:04 +00001000 const char
cristy11151212009-09-14 13:10:15 +00001001 *option;
cristycaec74e2009-09-14 02:20:04 +00001002
cristy3ed852e2009-09-05 21:47:34 +00001003 ErrorManager
1004 error_manager;
1005
cristy3ed852e2009-09-05 21:47:34 +00001006 Image
1007 *image;
1008
cristy3ed852e2009-09-05 21:47:34 +00001009 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00001010 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001011
1012 JSAMPROW
1013 scanline[1];
1014
1015 MagickBooleanType
1016 debug,
1017 status;
1018
1019 MagickSizeType
1020 number_pixels;
1021
cristy22646e22013-06-23 16:34:03 +00001022 MemoryInfo
1023 *memory_info;
1024
cristy4c08aed2011-07-01 19:47:50 +00001025 Quantum
1026 index;
1027
cristybb503372010-05-27 20:51:26 +00001028 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001029 i;
1030
1031 struct jpeg_decompress_struct
1032 jpeg_info;
1033
1034 struct jpeg_error_mgr
1035 jpeg_error;
1036
1037 register JSAMPLE
1038 *p;
1039
cristybb503372010-05-27 20:51:26 +00001040 size_t
cristy3ed852e2009-09-05 21:47:34 +00001041 units;
1042
cristy524222d2011-04-25 00:37:06 +00001043 ssize_t
1044 y;
1045
cristy3ed852e2009-09-05 21:47:34 +00001046 /*
1047 Open image file.
1048 */
1049 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001050 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001051 if (image_info->debug != MagickFalse)
1052 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1053 image_info->filename);
1054 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001055 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001056 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +00001057 (void) debug;
cristy9950d572011-10-01 18:22:35 +00001058 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001059 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1060 if (status == MagickFalse)
1061 {
1062 image=DestroyImageList(image);
1063 return((Image *) NULL);
1064 }
1065 /*
1066 Initialize JPEG parameters.
1067 */
cristy91044972011-04-22 14:21:16 +00001068 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001069 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1070 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1071 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001072 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +00001073 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy72f87f82013-06-23 16:43:05 +00001074 memory_info=(MemoryInfo *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001075 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001076 error_manager.image=image;
1077 if (setjmp(error_manager.error_recovery) != 0)
1078 {
1079 jpeg_destroy_decompress(&jpeg_info);
cristyf551aee2012-09-27 21:45:23 +00001080 if (error_manager.profile != (StringInfo *) NULL)
1081 error_manager.profile=DestroyStringInfo(error_manager.profile);
cristy3ed852e2009-09-05 21:47:34 +00001082 (void) CloseBlob(image);
1083 number_pixels=(MagickSizeType) image->columns*image->rows;
1084 if (number_pixels != 0)
1085 return(GetFirstImageInList(image));
1086 return(DestroyImage(image));
1087 }
1088 jpeg_info.client_data=(void *) &error_manager;
1089 jpeg_create_decompress(&jpeg_info);
1090 JPEGSourceManager(&jpeg_info,image);
1091 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
cristy00db0212014-06-25 23:01:19 +00001092 option=GetImageOption(image_info,"profile:skip");
cristy53b6cc32014-06-25 13:13:33 +00001093 if (IsOptionMember("ICC",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001094 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
cristy53b6cc32014-06-25 13:13:33 +00001095 if (IsOptionMember("IPTC",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001096 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
cristy3ed852e2009-09-05 21:47:34 +00001097 for (i=1; i < 16; i++)
1098 if ((i != 2) && (i != 13) && (i != 14))
cristy53b6cc32014-06-25 13:13:33 +00001099 if (IsOptionMember("APP",option) == MagickFalse)
cristy9056b872014-06-24 17:30:15 +00001100 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristydf878012014-01-12 23:39:40 +00001101 i=(ssize_t) jpeg_read_header(&jpeg_info,TRUE);
cristy53215c82009-09-19 16:32:36 +00001102 if ((image_info->colorspace == YCbCrColorspace) ||
1103 (image_info->colorspace == Rec601YCbCrColorspace) ||
1104 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001105 jpeg_info.out_color_space=JCS_YCbCr;
1106 /*
1107 Set image resolution.
1108 */
1109 units=0;
1110 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1111 (jpeg_info.Y_density != 1))
1112 {
cristy2a11bef2011-10-28 18:33:11 +00001113 image->resolution.x=(double) jpeg_info.X_density;
1114 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001115 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001116 }
1117 if (units == 1)
1118 image->units=PixelsPerInchResolution;
1119 if (units == 2)
1120 image->units=PixelsPerCentimeterResolution;
1121 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy092ec8d2013-04-26 13:46:22 +00001122 option=GetImageOption(image_info,"jpeg:size");
cristy7e7d8e92014-10-25 22:24:51 +00001123 if ((option != (const char *) NULL) &&
1124 (jpeg_info.out_color_space != JCS_YCbCr))
cristy3ed852e2009-09-05 21:47:34 +00001125 {
1126 double
1127 scale_factor;
1128
cristycaec74e2009-09-14 02:20:04 +00001129 GeometryInfo
1130 geometry_info;
1131
cristy0adb4f92009-11-28 18:08:51 +00001132 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001133 flags;
1134
cristy3ed852e2009-09-05 21:47:34 +00001135 /*
cristycaec74e2009-09-14 02:20:04 +00001136 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001137 */
cristy11151212009-09-14 13:10:15 +00001138 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001139 if ((flags & SigmaValue) == 0)
1140 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001141 jpeg_calc_output_dimensions(&jpeg_info);
1142 image->magick_columns=jpeg_info.output_width;
1143 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001144 scale_factor=1.0;
1145 if (geometry_info.rho != 0.0)
1146 scale_factor=jpeg_info.output_width/geometry_info.rho;
1147 if ((geometry_info.sigma != 0.0) &&
1148 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1149 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001150 jpeg_info.scale_num=1U;
1151 jpeg_info.scale_denom=(unsigned int) scale_factor;
1152 jpeg_calc_output_dimensions(&jpeg_info);
1153 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1155 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001156 }
cristy3ed852e2009-09-05 21:47:34 +00001157#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1158#if defined(D_LOSSLESS_SUPPORTED)
1159 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1160 JPEGInterlace : NoInterlace;
1161 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1162 LosslessJPEGCompression : JPEGCompression;
1163 if (jpeg_info.data_precision > 8)
1164 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1165 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1166 image->filename);
1167 if (jpeg_info.data_precision == 16)
1168 jpeg_info.data_precision=12;
1169#else
1170 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1171 NoInterlace;
1172 image->compression=JPEGCompression;
1173#endif
1174#else
1175 image->compression=JPEGCompression;
1176 image->interlace=JPEGInterlace;
1177#endif
cristy092ec8d2013-04-26 13:46:22 +00001178 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001179 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001180 {
cristyb4bb39c2012-02-21 18:45:54 +00001181 /*
1182 Let the JPEG library quantize the image.
1183 */
cristydf878012014-01-12 23:39:40 +00001184 jpeg_info.quantize_colors=TRUE;
anthony41906552011-10-07 12:15:27 +00001185 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001186 }
cristy092ec8d2013-04-26 13:46:22 +00001187 option=GetImageOption(image_info,"jpeg:block-smoothing");
cristy0d737182013-04-04 00:22:57 +00001188 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001189 jpeg_info.do_block_smoothing=IsStringTrue(option) != MagickFalse ? TRUE :
1190 FALSE;
cristyb4bb39c2012-02-21 18:45:54 +00001191 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00001192 option=GetImageOption(image_info,"jpeg:dct-method");
cristy787d4352010-03-06 13:55:58 +00001193 if (option != (const char *) NULL)
1194 switch (*option)
1195 {
1196 case 'D':
1197 case 'd':
1198 {
1199 if (LocaleCompare(option,"default") == 0)
1200 jpeg_info.dct_method=JDCT_DEFAULT;
1201 break;
1202 }
1203 case 'F':
1204 case 'f':
1205 {
1206 if (LocaleCompare(option,"fastest") == 0)
1207 jpeg_info.dct_method=JDCT_FASTEST;
1208 if (LocaleCompare(option,"float") == 0)
1209 jpeg_info.dct_method=JDCT_FLOAT;
1210 break;
1211 }
1212 case 'I':
1213 case 'i':
1214 {
1215 if (LocaleCompare(option,"ifast") == 0)
1216 jpeg_info.dct_method=JDCT_IFAST;
1217 if (LocaleCompare(option,"islow") == 0)
1218 jpeg_info.dct_method=JDCT_ISLOW;
1219 break;
1220 }
1221 }
cristy092ec8d2013-04-26 13:46:22 +00001222 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
cristy0d737182013-04-04 00:22:57 +00001223 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001224 jpeg_info.do_fancy_upsampling=IsStringTrue(option) != MagickFalse ? TRUE :
1225 FALSE;
cristy3ed852e2009-09-05 21:47:34 +00001226 (void) jpeg_start_decompress(&jpeg_info);
1227 image->columns=jpeg_info.output_width;
1228 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001229 image->depth=(size_t) jpeg_info.data_precision;
cristy93ff2cc2012-05-13 21:03:53 +00001230 switch (jpeg_info.out_color_space)
1231 {
1232 case JCS_RGB:
1233 default:
1234 {
cristyf551aee2012-09-27 21:45:23 +00001235 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001236 break;
1237 }
1238 case JCS_GRAYSCALE:
1239 {
cristy1a45be72013-04-07 00:16:24 +00001240 (void) SetImageColorspace(image,GRAYColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001241 break;
1242 }
1243 case JCS_YCbCr:
1244 {
cristyf551aee2012-09-27 21:45:23 +00001245 (void) SetImageColorspace(image,YCbCrColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001246 break;
1247 }
1248 case JCS_CMYK:
1249 {
cristyf551aee2012-09-27 21:45:23 +00001250 (void) SetImageColorspace(image,CMYKColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001251 break;
1252 }
1253 }
cristy36fc5502012-05-22 11:32:23 +00001254 if (IsITUFaxImage(image) != MagickFalse)
1255 {
cristyf551aee2012-09-27 21:45:23 +00001256 (void) SetImageColorspace(image,LabColorspace,exception);
cristy36fc5502012-05-22 11:32:23 +00001257 jpeg_info.out_color_space=JCS_YCbCr;
1258 }
cristy092ec8d2013-04-26 13:46:22 +00001259 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001260 if (option != (const char *) NULL)
1261 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1262 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001263 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1264 if ((jpeg_info.output_components == 1) &&
1265 (jpeg_info.quantize_colors == MagickFalse))
1266 {
cristybb503372010-05-27 20:51:26 +00001267 size_t
cristy3ed852e2009-09-05 21:47:34 +00001268 colors;
1269
cristybb503372010-05-27 20:51:26 +00001270 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001271 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001272 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1273 }
1274 if (image->debug != MagickFalse)
1275 {
1276 if (image->interlace != NoInterlace)
1277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1278 "Interlace: progressive");
1279 else
1280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1281 "Interlace: nonprogressive");
1282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1283 (int) jpeg_info.data_precision);
1284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1285 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1286 }
dirk881355a2013-08-19 19:56:50 +00001287 JPEGSetImageQuality(&jpeg_info,image,exception);
cristyd15e6592011-10-15 00:13:06 +00001288 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristy151b66d2015-04-15 10:50:31 +00001289 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001290 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001291 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001292 if (image_info->ping != MagickFalse)
1293 {
1294 jpeg_destroy_decompress(&jpeg_info);
1295 (void) CloseBlob(image);
1296 return(GetFirstImageInList(image));
1297 }
cristyacabb842014-12-14 23:36:33 +00001298 status=SetImageExtent(image,image->columns,image->rows,exception);
1299 if (status == MagickFalse)
cristy457f81d2015-02-01 18:13:23 +00001300 {
1301 jpeg_destroy_decompress(&jpeg_info);
1302 return(DestroyImageList(image));
1303 }
1304 if ((jpeg_info.output_components != 1) &&
1305 (jpeg_info.output_components != 3) && (jpeg_info.output_components != 4))
1306 {
1307 jpeg_destroy_decompress(&jpeg_info);
1308 ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
1309 }
cristy22646e22013-06-23 16:34:03 +00001310 memory_info=AcquireVirtualMemory((size_t) image->columns,
1311 jpeg_info.output_components*sizeof(*jpeg_pixels));
1312 if (memory_info == (MemoryInfo *) NULL)
cristyee1bdaa2013-07-16 16:45:03 +00001313 {
1314 jpeg_destroy_decompress(&jpeg_info);
1315 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1316 }
cristy22646e22013-06-23 16:34:03 +00001317 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001318 /*
1319 Convert JPEG pixels to pixel packets.
1320 */
1321 if (setjmp(error_manager.error_recovery) != 0)
1322 {
cristy22646e22013-06-23 16:34:03 +00001323 if (memory_info != (MemoryInfo *) NULL)
1324 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001325 jpeg_destroy_decompress(&jpeg_info);
1326 (void) CloseBlob(image);
1327 number_pixels=(MagickSizeType) image->columns*image->rows;
1328 if (number_pixels != 0)
1329 return(GetFirstImageInList(image));
1330 return(DestroyImage(image));
1331 }
1332 if (jpeg_info.quantize_colors != MagickFalse)
1333 {
cristybb503372010-05-27 20:51:26 +00001334 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001335 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001336 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001337 {
cristy1b58f252012-03-01 01:41:41 +00001338 image->colormap[i].red=(double) ScaleCharToQuantum(
1339 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001340 image->colormap[i].green=image->colormap[i].red;
1341 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001342 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001343 }
1344 else
cristybb503372010-05-27 20:51:26 +00001345 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001346 {
cristy1b58f252012-03-01 01:41:41 +00001347 image->colormap[i].red=(double) ScaleCharToQuantum(
1348 jpeg_info.colormap[0][i]);
1349 image->colormap[i].green=(double) ScaleCharToQuantum(
1350 jpeg_info.colormap[1][i]);
1351 image->colormap[i].blue=(double) ScaleCharToQuantum(
1352 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001353 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001354 }
1355 }
1356 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001357 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001358 {
cristybb503372010-05-27 20:51:26 +00001359 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001360 x;
1361
cristy4c08aed2011-07-01 19:47:50 +00001362 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001363 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001364
1365 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1366 {
1367 (void) ThrowMagickException(exception,GetMagickModule(),
1368 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1369 continue;
1370 }
1371 p=jpeg_pixels;
1372 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001373 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001374 break;
cristy3ed852e2009-09-05 21:47:34 +00001375 if (jpeg_info.data_precision > 8)
1376 {
cristy5328a4c2013-12-03 11:32:13 +00001377 unsigned short
1378 scale;
1379
cristy477b9fd2013-12-03 16:52:31 +00001380 scale=65535U/GetQuantumRange(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 {
cristybb503372010-05-27 20:51:26 +00001384 size_t
cristy3ed852e2009-09-05 21:47:34 +00001385 pixel;
1386
cristy5328a4c2013-12-03 11:32:13 +00001387 pixel=(size_t) (scale*GETJSAMPLE(*p));
cristyc82a27b2011-10-21 01:07:16 +00001388 index=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 {
cristy217ddd62013-12-06 23:27:09 +00001398 SetPixelRed(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1399 q);
1400 SetPixelGreen(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1401 q);
1402 SetPixelBlue(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1403 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 {
cristy217ddd62013-12-06 23:27:09 +00001410 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(scale*
1411 GETJSAMPLE(*p++)),q);
1412 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(scale*
1413 GETJSAMPLE(*p++)),q);
1414 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(scale*
1415 GETJSAMPLE(*p++)),q);
1416 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(scale*
1417 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 {
cristyc82a27b2011-10-21 01:07:16 +00001426 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001427 SetPixelIndex(image,index,q);
cristy11a06d32015-01-04 12:03:27 +00001428 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001429 p++;
cristyed231572011-07-14 02:18:59 +00001430 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001431 }
1432 else
1433 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001434 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001435 {
cristy4c08aed2011-07-01 19:47:50 +00001436 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1437 GETJSAMPLE(*p++)),q);
1438 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1439 GETJSAMPLE(*p++)),q);
1440 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1441 GETJSAMPLE(*p++)),q);
1442 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001443 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001444 }
1445 else
cristybb503372010-05-27 20:51:26 +00001446 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001447 {
cristy4c08aed2011-07-01 19:47:50 +00001448 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1449 (unsigned char) GETJSAMPLE(*p++)),q);
1450 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1451 (unsigned char) GETJSAMPLE(*p++)),q);
1452 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1453 (unsigned char) GETJSAMPLE(*p++)),q);
1454 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1455 (unsigned char) GETJSAMPLE(*p++)),q);
1456 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001457 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001458 }
1459 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1460 break;
cristy524222d2011-04-25 00:37:06 +00001461 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1462 image->rows);
1463 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001464 {
1465 jpeg_abort_decompress(&jpeg_info);
1466 break;
1467 }
cristy3ed852e2009-09-05 21:47:34 +00001468 }
cristyd28b1dd2011-05-14 20:30:38 +00001469 if (status != MagickFalse)
1470 {
1471 error_manager.finished=MagickTrue;
1472 if (setjmp(error_manager.error_recovery) == 0)
1473 (void) jpeg_finish_decompress(&jpeg_info);
1474 }
cristy3ed852e2009-09-05 21:47:34 +00001475 /*
1476 Free jpeg resources.
1477 */
cristy3ed852e2009-09-05 21:47:34 +00001478 jpeg_destroy_decompress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00001479 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001480 (void) CloseBlob(image);
1481 return(GetFirstImageInList(image));
1482}
1483#endif
cristy86b6e552015-06-06 13:25:16 +00001484
cristy3ed852e2009-09-05 21:47:34 +00001485/*
1486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487% %
1488% %
1489% %
1490% R e g i s t e r J P E G I m a g e %
1491% %
1492% %
1493% %
1494%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1495%
1496% RegisterJPEGImage() adds properties for the JPEG image format to
1497% the list of supported formats. The properties include the image format
1498% tag, a method to read and/or write the format, whether the format
1499% supports the saving of more than one frame to the same file or blob,
1500% whether the format supports native in-memory I/O, and a brief
1501% description of the format.
1502%
1503% The format of the RegisterJPEGImage method is:
1504%
cristybb503372010-05-27 20:51:26 +00001505% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001506%
1507*/
cristybb503372010-05-27 20:51:26 +00001508ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001509{
dirk06b627a2015-04-06 18:59:17 +00001510#define JPEGDescription "Joint Photographic Experts Group JFIF format"
1511
cristy3ed852e2009-09-05 21:47:34 +00001512 char
cristy151b66d2015-04-15 10:50:31 +00001513 version[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001514
1515 MagickInfo
1516 *entry;
1517
cristy3ed852e2009-09-05 21:47:34 +00001518 *version='\0';
1519#if defined(JPEG_LIB_VERSION)
cristy151b66d2015-04-15 10:50:31 +00001520 (void) FormatLocaleString(version,MagickPathExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001521#endif
dirk06b627a2015-04-06 18:59:17 +00001522 entry=AcquireMagickInfo("JPEG","JPE",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001523#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristyb338b712014-11-27 14:06:34 +00001524 entry->thread_support=NoThreadSupport;
1525#endif
1526#if defined(MAGICKCORE_JPEG_DELEGATE)
1527 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1528 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1529#endif
1530 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001531 entry->flags^=CoderAdjoinFlag;
1532 entry->flags^=CoderUseExtensionFlag;
cristyb338b712014-11-27 14:06:34 +00001533 if (*version != '\0')
1534 entry->version=ConstantString(version);
1535 entry->mime_type=ConstantString("image/jpeg");
cristyb338b712014-11-27 14:06:34 +00001536 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001537 entry=AcquireMagickInfo("JPEG","JPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001538#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001539 entry->thread_support=NoThreadSupport;
1540#endif
cristy3ed852e2009-09-05 21:47:34 +00001541#if defined(MAGICKCORE_JPEG_DELEGATE)
1542 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1543 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1544#endif
1545 entry->magick=(IsImageFormatHandler *) IsJPEG;
dirk08e9a112015-02-22 01:51:41 +00001546 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +00001547 if (*version != '\0')
1548 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001549 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001550 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001551 entry=AcquireMagickInfo("JPEG","JPG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001552#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001553 entry->thread_support=NoThreadSupport;
1554#endif
cristy3ed852e2009-09-05 21:47:34 +00001555#if defined(MAGICKCORE_JPEG_DELEGATE)
1556 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1557 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1558#endif
dirk08e9a112015-02-22 01:51:41 +00001559 entry->flags^=CoderAdjoinFlag;
1560 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001561 if (*version != '\0')
1562 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001563 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001564 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001565 entry=AcquireMagickInfo("JPEG","JPS",JPEGDescription);
cristya663a4f2015-01-23 12:11:57 +00001566#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1567 entry->thread_support=NoThreadSupport;
1568#endif
1569#if defined(MAGICKCORE_JPEG_DELEGATE)
1570 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1571 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1572#endif
dirk08e9a112015-02-22 01:51:41 +00001573 entry->flags^=CoderAdjoinFlag;
1574 entry->flags^=CoderUseExtensionFlag;
cristya663a4f2015-01-23 12:11:57 +00001575 if (*version != '\0')
1576 entry->version=ConstantString(version);
1577 entry->mime_type=ConstantString("image/jpeg");
cristya663a4f2015-01-23 12:11:57 +00001578 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001579 entry=AcquireMagickInfo("JPEG","PJPEG",JPEGDescription);
cristy8e62a802014-12-27 22:43:35 +00001580#if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
cristy61c382d2014-09-17 11:18:11 +00001581 entry->thread_support=NoThreadSupport;
1582#endif
cristy3ed852e2009-09-05 21:47:34 +00001583#if defined(MAGICKCORE_JPEG_DELEGATE)
1584 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1585 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1586#endif
dirk08e9a112015-02-22 01:51:41 +00001587 entry->flags^=CoderAdjoinFlag;
1588 entry->flags^=CoderUseExtensionFlag;
cristy3ed852e2009-09-05 21:47:34 +00001589 if (*version != '\0')
1590 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001591 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001592 (void) RegisterMagickInfo(entry);
1593 return(MagickImageCoderSignature);
1594}
cristy86b6e552015-06-06 13:25:16 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596/*
1597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1598% %
1599% %
1600% %
1601% U n r e g i s t e r J P E G I m a g e %
1602% %
1603% %
1604% %
1605%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1606%
1607% UnregisterJPEGImage() removes format registrations made by the
1608% JPEG module from the list of supported formats.
1609%
1610% The format of the UnregisterJPEGImage method is:
1611%
1612% UnregisterJPEGImage(void)
1613%
1614*/
1615ModuleExport void UnregisterJPEGImage(void)
1616{
1617 (void) UnregisterMagickInfo("PJPG");
cristya663a4f2015-01-23 12:11:57 +00001618 (void) UnregisterMagickInfo("JPS");
cristy3ed852e2009-09-05 21:47:34 +00001619 (void) UnregisterMagickInfo("JPG");
cristya663a4f2015-01-23 12:11:57 +00001620 (void) UnregisterMagickInfo("JPEG");
cristyb338b712014-11-27 14:06:34 +00001621 (void) UnregisterMagickInfo("JPE");
cristy3ed852e2009-09-05 21:47:34 +00001622}
cristy86b6e552015-06-06 13:25:16 +00001623
cristy3ed852e2009-09-05 21:47:34 +00001624#if defined(MAGICKCORE_JPEG_DELEGATE)
1625/*
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627% %
1628% %
1629% %
1630% W r i t e J P E G I m a g e %
1631% %
1632% %
1633% %
1634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1635%
1636% WriteJPEGImage() writes a JPEG image file and returns it. It
1637% allocates the memory necessary for the new Image structure and returns a
1638% pointer to the new image.
1639%
1640% The format of the WriteJPEGImage method is:
1641%
cristy91044972011-04-22 14:21:16 +00001642% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001643% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001644%
1645% A description of each parameter follows:
1646%
1647% o image_info: the image info.
1648%
1649% o jpeg_image: The image.
1650%
cristy1e178e72011-08-28 19:44:34 +00001651% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001652%
1653*/
1654
cristy1b58f252012-03-01 01:41:41 +00001655static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1656{
1657 assert(table != (QuantizationTable *) NULL);
1658 if (table->slot != (char *) NULL)
1659 table->slot=DestroyString(table->slot);
1660 if (table->description != (char *) NULL)
1661 table->description=DestroyString(table->description);
1662 if (table->levels != (unsigned int *) NULL)
1663 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1664 table=(QuantizationTable *) RelinquishMagickMemory(table);
1665 return(table);
1666}
1667
cristy3ed852e2009-09-05 21:47:34 +00001668static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1669{
1670 DestinationManager
1671 *destination;
1672
1673 destination=(DestinationManager *) cinfo->dest;
1674 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1675 MaxBufferExtent,destination->buffer);
1676 if (destination->manager.free_in_buffer != MaxBufferExtent)
1677 ERREXIT(cinfo,JERR_FILE_WRITE);
1678 destination->manager.next_output_byte=destination->buffer;
1679 return(TRUE);
1680}
1681
cristy1b58f252012-03-01 01:41:41 +00001682static QuantizationTable *GetQuantizationTable(const char *filename,
1683 const char *slot,ExceptionInfo *exception)
1684{
1685 char
1686 *p,
1687 *xml;
1688
1689 const char
1690 *attribute,
1691 *content;
1692
1693 double
1694 value;
1695
1696 register ssize_t
1697 i;
1698
1699 ssize_t
1700 j;
1701
1702 QuantizationTable
1703 *table;
1704
cristy1fcc8b62012-03-02 17:25:55 +00001705 size_t
1706 length;
1707
cristy1b58f252012-03-01 01:41:41 +00001708 XMLTreeInfo
1709 *description,
1710 *levels,
1711 *quantization_tables,
1712 *table_iterator;
1713
1714 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1715 "Loading quantization tables \"%s\" ...",filename);
1716 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001717 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001718 if (xml == (char *) NULL)
1719 return(table);
1720 quantization_tables=NewXMLTree(xml,exception);
1721 if (quantization_tables == (XMLTreeInfo *) NULL)
1722 {
1723 xml=DestroyString(xml);
1724 return(table);
1725 }
1726 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1727 table_iterator != (XMLTreeInfo *) NULL;
1728 table_iterator=GetNextXMLTreeTag(table_iterator))
1729 {
1730 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1731 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1732 break;
1733 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1734 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1735 break;
1736 }
1737 if (table_iterator == (XMLTreeInfo *) NULL)
1738 {
1739 xml=DestroyString(xml);
1740 return(table);
1741 }
1742 description=GetXMLTreeChild(table_iterator,"description");
1743 if (description == (XMLTreeInfo *) NULL)
1744 {
1745 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1746 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1747 quantization_tables=DestroyXMLTree(quantization_tables);
1748 xml=DestroyString(xml);
1749 return(table);
1750 }
1751 levels=GetXMLTreeChild(table_iterator,"levels");
1752 if (levels == (XMLTreeInfo *) NULL)
1753 {
1754 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1755 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1756 quantization_tables=DestroyXMLTree(quantization_tables);
1757 xml=DestroyString(xml);
1758 return(table);
1759 }
1760 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1761 if (table == (QuantizationTable *) NULL)
1762 ThrowFatalException(ResourceLimitFatalError,
1763 "UnableToAcquireQuantizationTable");
1764 table->slot=(char *) NULL;
1765 table->description=(char *) NULL;
1766 table->levels=(unsigned int *) NULL;
1767 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1768 if (attribute != (char *) NULL)
1769 table->slot=ConstantString(attribute);
1770 content=GetXMLTreeContent(description);
1771 if (content != (char *) NULL)
1772 table->description=ConstantString(content);
1773 attribute=GetXMLTreeAttribute(levels,"width");
1774 if (attribute == (char *) NULL)
1775 {
1776 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1777 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1778 quantization_tables=DestroyXMLTree(quantization_tables);
1779 table=DestroyQuantizationTable(table);
1780 xml=DestroyString(xml);
1781 return(table);
1782 }
1783 table->width=StringToUnsignedLong(attribute);
1784 if (table->width == 0)
1785 {
1786 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1787 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1788 quantization_tables=DestroyXMLTree(quantization_tables);
1789 table=DestroyQuantizationTable(table);
1790 xml=DestroyString(xml);
1791 return(table);
1792 }
1793 attribute=GetXMLTreeAttribute(levels,"height");
1794 if (attribute == (char *) NULL)
1795 {
1796 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1797 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1798 quantization_tables=DestroyXMLTree(quantization_tables);
1799 table=DestroyQuantizationTable(table);
1800 xml=DestroyString(xml);
1801 return(table);
1802 }
1803 table->height=StringToUnsignedLong(attribute);
1804 if (table->height == 0)
1805 {
1806 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1807 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1808 quantization_tables=DestroyXMLTree(quantization_tables);
1809 table=DestroyQuantizationTable(table);
1810 xml=DestroyString(xml);
1811 return(table);
1812 }
1813 attribute=GetXMLTreeAttribute(levels,"divisor");
1814 if (attribute == (char *) NULL)
1815 {
1816 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1817 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1818 quantization_tables=DestroyXMLTree(quantization_tables);
1819 table=DestroyQuantizationTable(table);
1820 xml=DestroyString(xml);
1821 return(table);
1822 }
cristy043f3f32012-03-02 17:37:28 +00001823 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1824 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001825 {
1826 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1827 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1828 quantization_tables=DestroyXMLTree(quantization_tables);
1829 table=DestroyQuantizationTable(table);
1830 xml=DestroyString(xml);
1831 return(table);
1832 }
1833 content=GetXMLTreeContent(levels);
1834 if (content == (char *) NULL)
1835 {
1836 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1837 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1838 quantization_tables=DestroyXMLTree(quantization_tables);
1839 table=DestroyQuantizationTable(table);
1840 xml=DestroyString(xml);
1841 return(table);
1842 }
cristy1fcc8b62012-03-02 17:25:55 +00001843 length=(size_t) table->width*table->height;
1844 if (length < 64)
1845 length=64;
cristy092006b2012-03-02 17:26:02 +00001846 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001847 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001848 if (table->levels == (unsigned int *) NULL)
1849 ThrowFatalException(ResourceLimitFatalError,
1850 "UnableToAcquireQuantizationTable");
1851 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1852 {
cristy043f3f32012-03-02 17:37:28 +00001853 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1854 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001855 while (isspace((int) ((unsigned char) *p)) != 0)
1856 p++;
1857 if (*p == ',')
1858 p++;
1859 content=p;
1860 }
cristy043f3f32012-03-02 17:37:28 +00001861 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001862 (void) value;
1863 if (p != content)
1864 {
1865 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1866 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1867 quantization_tables=DestroyXMLTree(quantization_tables);
1868 table=DestroyQuantizationTable(table);
1869 xml=DestroyString(xml);
1870 return(table);
1871 }
cristy043f3f32012-03-02 17:37:28 +00001872 for (j=i; j < 64; j++)
1873 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001874 quantization_tables=DestroyXMLTree(quantization_tables);
1875 xml=DestroyString(xml);
1876 return(table);
1877}
1878
cristy3ed852e2009-09-05 21:47:34 +00001879static void InitializeDestination(j_compress_ptr cinfo)
1880{
1881 DestinationManager
1882 *destination;
1883
1884 destination=(DestinationManager *) cinfo->dest;
1885 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1886 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1887 destination->manager.next_output_byte=destination->buffer;
1888 destination->manager.free_in_buffer=MaxBufferExtent;
1889}
1890
cristy3ed852e2009-09-05 21:47:34 +00001891static void TerminateDestination(j_compress_ptr cinfo)
1892{
1893 DestinationManager
1894 *destination;
1895
1896 destination=(DestinationManager *) cinfo->dest;
1897 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1898 {
1899 ssize_t
1900 count;
1901
1902 count=WriteBlob(destination->image,MaxBufferExtent-
1903 destination->manager.free_in_buffer,destination->buffer);
1904 if (count != (ssize_t)
1905 (MaxBufferExtent-destination->manager.free_in_buffer))
1906 ERREXIT(cinfo,JERR_FILE_WRITE);
1907 }
1908}
1909
1910static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1911{
1912 const char
1913 *name;
1914
1915 const StringInfo
1916 *profile;
1917
1918 MagickBooleanType
1919 iptc;
1920
cristybb503372010-05-27 20:51:26 +00001921 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001922 i;
1923
1924 size_t
cristy524222d2011-04-25 00:37:06 +00001925 length,
1926 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001927
1928 StringInfo
1929 *custom_profile;
1930
cristy3ed852e2009-09-05 21:47:34 +00001931 /*
1932 Save image profile as a APP marker.
1933 */
1934 iptc=MagickFalse;
1935 custom_profile=AcquireStringInfo(65535L);
1936 ResetImageProfileIterator(image);
1937 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1938 {
cristy109e5572010-09-16 18:38:17 +00001939 register unsigned char
1940 *p;
1941
cristy3ed852e2009-09-05 21:47:34 +00001942 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001943 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001944 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001945 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001946 {
1947 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1948 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1949 (unsigned int) length);
1950 }
1951 if (LocaleCompare(name,"ICC") == 0)
1952 {
1953 register unsigned char
1954 *p;
1955
cristy8ba82ae2013-05-29 14:19:49 +00001956 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001957 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001958 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001959 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001960 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001961 {
1962 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001963 p[12]=(unsigned char) ((i/65519L)+1);
1964 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001965 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001966 length);
1967 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001968 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001969 }
1970 }
1971 if (((LocaleCompare(name,"IPTC") == 0) ||
1972 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1973 {
cristybb503372010-05-27 20:51:26 +00001974 size_t
cristy3ed852e2009-09-05 21:47:34 +00001975 roundup;
1976
1977 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001978 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001979 {
1980 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001981 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001982 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1983 {
1984 (void) memcpy(p,"Photoshop 3.0 ",14);
1985 tag_length=14;
1986 }
1987 else
1988 {
1989 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1990 tag_length=26;
1991 p[24]=(unsigned char) (length >> 8);
1992 p[25]=(unsigned char) (length & 0xff);
1993 }
1994 p[13]=0x00;
1995 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001996 if (roundup != 0)
1997 p[length+tag_length]='\0';
1998 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1999 custom_profile),(unsigned int) (length+tag_length+roundup));
2000 }
2001 }
2002 if (LocaleCompare(name,"XMP") == 0)
2003 {
2004 StringInfo
2005 *xmp_profile;
2006
2007 /*
2008 Add namespace to XMP profile.
2009 */
cristy0615f0e2011-10-12 11:36:46 +00002010 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy30841e62014-05-19 00:45:15 +00002011 if (xmp_profile != (StringInfo *) NULL)
cristy8418c7e2014-05-18 18:38:26 +00002012 {
cristyc08a0aa2014-05-25 23:30:09 +00002013 if (profile != (StringInfo *) NULL)
2014 ConcatenateStringInfo(xmp_profile,profile);
cristy8418c7e2014-05-18 18:38:26 +00002015 GetStringInfoDatum(xmp_profile)[28]='\0';
2016 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
2017 {
2018 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
2019 jpeg_write_marker(jpeg_info,XML_MARKER,
2020 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
2021 }
2022 xmp_profile=DestroyStringInfo(xmp_profile);
2023 }
cristy3ed852e2009-09-05 21:47:34 +00002024 }
cristye8c25f92010-06-03 00:53:06 +00002025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2026 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00002027 name=GetNextImageProfile(image);
2028 }
2029 custom_profile=DestroyStringInfo(custom_profile);
2030}
2031
2032static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
2033{
2034 DestinationManager
2035 *destination;
2036
2037 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
2038 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
2039 destination=(DestinationManager *) cinfo->dest;
2040 destination->manager.init_destination=InitializeDestination;
2041 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2042 destination->manager.term_destination=TerminateDestination;
2043 destination->image=image;
2044}
2045
2046static char **SamplingFactorToList(const char *text)
2047{
2048 char
2049 **textlist;
2050
2051 register char
2052 *q;
2053
2054 register const char
2055 *p;
2056
cristybb503372010-05-27 20:51:26 +00002057 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002058 i;
2059
cristybb503372010-05-27 20:51:26 +00002060 size_t
cristy3ed852e2009-09-05 21:47:34 +00002061 lines;
2062
2063 if (text == (char *) NULL)
2064 return((char **) NULL);
2065 /*
2066 Convert string to an ASCII list.
2067 */
2068 lines=1;
2069 for (p=text; *p != '\0'; p++)
2070 if (*p == ',')
2071 lines++;
cristy151b66d2015-04-15 10:50:31 +00002072 textlist=(char **) AcquireQuantumMemory((size_t) lines+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00002073 sizeof(*textlist));
2074 if (textlist == (char **) NULL)
2075 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2076 p=text;
cristybb503372010-05-27 20:51:26 +00002077 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00002078 {
2079 for (q=(char *) p; *q != '\0'; q++)
2080 if (*q == ',')
2081 break;
cristy151b66d2015-04-15 10:50:31 +00002082 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00002083 sizeof(*textlist[i]));
2084 if (textlist[i] == (char *) NULL)
2085 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2086 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2087 if (*q == '\r')
2088 q++;
2089 p=q+1;
2090 }
2091 textlist[i]=(char *) NULL;
2092 return(textlist);
2093}
2094
2095static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002096 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002097{
2098 const char
2099 *option,
2100 *sampling_factor,
2101 *value;
2102
2103 ErrorManager
2104 error_manager;
2105
cristy86b6e552015-06-06 13:25:16 +00002106 Image
2107 *volatile volatile_image;
2108
cristy21749e92012-02-18 02:29:21 +00002109 int
cristy7b8ed292013-05-18 01:13:56 +00002110 colorspace,
cristy21749e92012-02-18 02:29:21 +00002111 quality;
2112
cristy3ed852e2009-09-05 21:47:34 +00002113 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002114 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002115
2116 JSAMPROW
2117 scanline[1];
2118
cristy3ed852e2009-09-05 21:47:34 +00002119 MagickBooleanType
2120 status;
2121
cristy22646e22013-06-23 16:34:03 +00002122 MemoryInfo
2123 *memory_info;
2124
cristy3ed852e2009-09-05 21:47:34 +00002125 register JSAMPLE
2126 *q;
2127
cristybb503372010-05-27 20:51:26 +00002128 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002129 i;
2130
cristy524222d2011-04-25 00:37:06 +00002131 ssize_t
2132 y;
2133
cristy3ed852e2009-09-05 21:47:34 +00002134 struct jpeg_compress_struct
2135 jpeg_info;
2136
2137 struct jpeg_error_mgr
2138 jpeg_error;
2139
cristy5328a4c2013-12-03 11:32:13 +00002140 unsigned short
2141 scale;
2142
cristy3ed852e2009-09-05 21:47:34 +00002143 /*
2144 Open image file.
2145 */
2146 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002147 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002148 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002149 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002150 if (image->debug != MagickFalse)
2151 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002152 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002153 assert(exception->signature == MagickCoreSignature);
cristya663a4f2015-01-23 12:11:57 +00002154 if ((LocaleCompare(image_info->magick,"JPS") == 0) &&
2155 (image->next != (Image *) NULL))
2156 image=AppendImages(image,MagickFalse,exception);
cristy73ef4eb2015-01-23 14:31:57 +00002157 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2158 if (status == MagickFalse)
2159 return(status);
cristy3ed852e2009-09-05 21:47:34 +00002160 /*
2161 Initialize JPEG parameters.
2162 */
cristy91044972011-04-22 14:21:16 +00002163 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002164 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2165 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
cristy86b6e552015-06-06 13:25:16 +00002166 volatile_image=image;
2167 jpeg_info.client_data=(void *) volatile_image;
cristy3ed852e2009-09-05 21:47:34 +00002168 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002169 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002170 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002171 error_manager.exception=exception;
cristy86b6e552015-06-06 13:25:16 +00002172 error_manager.image=volatile_image;
cristy72f87f82013-06-23 16:43:05 +00002173 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002174 if (setjmp(error_manager.error_recovery) != 0)
2175 {
2176 jpeg_destroy_compress(&jpeg_info);
cristy86b6e552015-06-06 13:25:16 +00002177 (void) CloseBlob(volatile_image);
cristy3ed852e2009-09-05 21:47:34 +00002178 return(MagickFalse);
2179 }
2180 jpeg_info.client_data=(void *) &error_manager;
2181 jpeg_create_compress(&jpeg_info);
2182 JPEGDestinationManager(&jpeg_info,image);
2183 if ((image->columns != (unsigned int) image->columns) ||
2184 (image->rows != (unsigned int) image->rows))
2185 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2186 jpeg_info.image_width=(unsigned int) image->columns;
2187 jpeg_info.image_height=(unsigned int) image->rows;
2188 jpeg_info.input_components=3;
2189 jpeg_info.data_precision=8;
2190 jpeg_info.in_color_space=JCS_RGB;
2191 switch (image->colorspace)
2192 {
2193 case CMYKColorspace:
2194 {
2195 jpeg_info.input_components=4;
2196 jpeg_info.in_color_space=JCS_CMYK;
2197 break;
2198 }
2199 case YCbCrColorspace:
2200 case Rec601YCbCrColorspace:
2201 case Rec709YCbCrColorspace:
2202 {
2203 jpeg_info.in_color_space=JCS_YCbCr;
2204 break;
2205 }
2206 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002207 {
cristye9355552014-01-16 13:35:05 +00002208 if (image_info->type == TrueColorType)
2209 break;
cristy3ed852e2009-09-05 21:47:34 +00002210 jpeg_info.input_components=1;
2211 jpeg_info.in_color_space=JCS_GRAYSCALE;
2212 break;
2213 }
2214 default:
cristy9f396782009-12-21 01:37:45 +00002215 {
cristyaf8d3912014-02-21 14:50:33 +00002216 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye9355552014-01-16 13:35:05 +00002217 if (image_info->type == TrueColorType)
2218 break;
dirkf1d85482015-04-06 00:36:00 +00002219 if (SetImageGray(image,exception) != MagickFalse)
cristye9355552014-01-16 13:35:05 +00002220 {
2221 jpeg_info.input_components=1;
2222 jpeg_info.in_color_space=JCS_GRAYSCALE;
2223 }
cristy3ed852e2009-09-05 21:47:34 +00002224 break;
cristy9f396782009-12-21 01:37:45 +00002225 }
cristy3ed852e2009-09-05 21:47:34 +00002226 }
cristy3ed852e2009-09-05 21:47:34 +00002227 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002228 if (jpeg_info.in_color_space == JCS_CMYK)
2229 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002230 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2231 jpeg_info.data_precision=8;
2232 else
dirk93b02b72013-11-16 16:03:36 +00002233 jpeg_info.data_precision=BITS_IN_JSAMPLE;
cristy3ed852e2009-09-05 21:47:34 +00002234 if (image->debug != MagickFalse)
2235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002236 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2237 floor(image->resolution.y+0.5));
2238 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002239 {
2240 /*
2241 Set image resolution.
2242 */
cristydf878012014-01-12 23:39:40 +00002243 jpeg_info.write_JFIF_header=TRUE;
cristy2a11bef2011-10-28 18:33:11 +00002244 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2245 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy0c220632013-09-25 22:10:28 +00002246 /*
2247 Set image resolution units.
2248 */
cristy3ed852e2009-09-05 21:47:34 +00002249 if (image->units == PixelsPerInchResolution)
2250 jpeg_info.density_unit=(UINT8) 1;
2251 if (image->units == PixelsPerCentimeterResolution)
2252 jpeg_info.density_unit=(UINT8) 2;
2253 }
cristy97cb3bf2012-02-21 18:31:20 +00002254 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002255 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002256 if (option != (const char *) NULL)
2257 switch (*option)
2258 {
2259 case 'D':
2260 case 'd':
2261 {
2262 if (LocaleCompare(option,"default") == 0)
2263 jpeg_info.dct_method=JDCT_DEFAULT;
2264 break;
2265 }
2266 case 'F':
2267 case 'f':
2268 {
2269 if (LocaleCompare(option,"fastest") == 0)
2270 jpeg_info.dct_method=JDCT_FASTEST;
2271 if (LocaleCompare(option,"float") == 0)
2272 jpeg_info.dct_method=JDCT_FLOAT;
2273 break;
2274 }
2275 case 'I':
2276 case 'i':
2277 {
2278 if (LocaleCompare(option,"ifast") == 0)
2279 jpeg_info.dct_method=JDCT_IFAST;
2280 if (LocaleCompare(option,"islow") == 0)
2281 jpeg_info.dct_method=JDCT_ISLOW;
2282 break;
2283 }
2284 }
cristy092ec8d2013-04-26 13:46:22 +00002285 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002286 if (option != (const char *) NULL)
cristyb9d0e7e2014-11-02 18:14:44 +00002287 jpeg_info.optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE :
2288 FALSE;
cristy7d31f6e2014-11-11 13:07:58 +00002289 else
2290 {
2291 MagickSizeType
2292 length;
2293
2294 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2295 image->rows*sizeof(JSAMPLE);
2296 if (length == (MagickSizeType) ((size_t) length))
2297 {
2298 /*
2299 Perform optimization only if available memory resources permit it.
2300 */
2301 status=AcquireMagickResource(MemoryResource,length);
2302 RelinquishMagickResource(MemoryResource,length);
2303 jpeg_info.optimize_coding=status == MagickFalse ? FALSE : TRUE;
2304 }
2305 }
cristy3ed852e2009-09-05 21:47:34 +00002306#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2307 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2308 (image_info->interlace != NoInterlace))
2309 {
2310 if (image->debug != MagickFalse)
2311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2312 "Interlace: progressive");
2313 jpeg_simple_progression(&jpeg_info);
2314 }
2315 else
2316 if (image->debug != MagickFalse)
2317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2318 "Interlace: non-progressive");
2319#else
2320 if (image->debug != MagickFalse)
2321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2322 "Interlace: nonprogressive");
2323#endif
dirk871abab2013-11-03 13:21:16 +00002324 quality=92;
cristy2a950402014-03-12 17:57:14 +00002325 if ((image_info->compression != LosslessJPEGCompression) &&
2326 (image->quality <= 100))
2327 {
2328 if (image->quality != UndefinedCompressionQuality)
2329 quality=(int) image->quality;
2330 if (image->debug != MagickFalse)
2331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2332 (double) image->quality);
2333 }
2334 else
2335 {
2336#if !defined(C_LOSSLESS_SUPPORTED)
2337 quality=100;
2338 if (image->debug != MagickFalse)
2339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2340#else
2341 if (image->quality < 100)
2342 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2343 "LosslessToLossyJPEGConversion",image->filename);
2344 else
2345 {
2346 int
2347 point_transform,
2348 predictor;
2349
2350 predictor=image->quality/100; /* range 1-7 */
2351 point_transform=image->quality % 20; /* range 0-15 */
2352 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2353 if (image->debug != MagickFalse)
2354 {
2355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2356 "Compression: lossless");
2357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2358 "Predictor: %d",predictor);
2359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2360 "Point Transform: %d",point_transform);
2361 }
2362 }
2363#endif
2364 }
cristy092ec8d2013-04-26 13:46:22 +00002365 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002366 if (option != (const char *) NULL)
2367 {
2368 Image
2369 *jpeg_image;
2370
2371 ImageInfo
2372 *jpeg_info;
2373
2374 jpeg_info=CloneImageInfo(image_info);
cristye0823d52013-11-03 21:50:10 +00002375 jpeg_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002376 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002377 if (jpeg_image != (Image *) NULL)
2378 {
2379 MagickSizeType
2380 extent;
2381
2382 size_t
cristy87e73ab2010-02-12 01:59:12 +00002383 maximum,
2384 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002385
2386 /*
2387 Search for compression quality that does not exceed image extent.
2388 */
2389 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002390 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002391 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002392 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
cristy4e8b2ed2015-03-06 19:54:18 +00002393 maximum=image->quality;
2394 if (maximum < 2)
2395 maximum=2;
dirkcf845242013-08-11 21:36:19 +00002396 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002397 {
cristy3975f402014-02-26 14:36:33 +00002398 (void) AcquireUniqueFilename(jpeg_image->filename);
2399 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
cristy1e178e72011-08-28 19:44:34 +00002400 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002401 if (GetBlobSize(jpeg_image) <= extent)
cristy3975f402014-02-26 14:36:33 +00002402 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002403 else
cristy3975f402014-02-26 14:36:33 +00002404 maximum=jpeg_image->quality-1;
2405 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristy0adb4f92009-11-28 18:08:51 +00002406 }
cristy58d4fe12015-02-07 11:53:05 +00002407 quality=(int) minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002408 jpeg_image=DestroyImage(jpeg_image);
2409 }
2410 jpeg_info=DestroyImageInfo(jpeg_info);
2411 }
cristy9bb7c842014-06-17 23:42:24 +00002412 jpeg_set_quality(&jpeg_info,quality,TRUE);
cristy16e74722012-07-01 13:01:38 +00002413#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002414 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002415 if (option != (const char *) NULL)
2416 {
2417 GeometryInfo
2418 geometry_info;
2419
2420 int
2421 flags;
2422
2423 /*
2424 Set quality scaling for luminance and chrominance separately.
2425 */
2426 flags=ParseGeometry(option,&geometry_info);
2427 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2428 {
2429 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2430 (geometry_info.rho+0.5));
2431 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2432 (geometry_info.sigma+0.5));
cristy9bb7c842014-06-17 23:42:24 +00002433 jpeg_default_qtables(&jpeg_info,TRUE);
cristy16e74722012-07-01 13:01:38 +00002434 }
2435 }
2436#endif
cristy7b8ed292013-05-18 01:13:56 +00002437 colorspace=jpeg_info.in_color_space;
2438 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002439 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002440 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002441 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002442 colorspace=StringToInteger(value);
2443 sampling_factor=(const char *) NULL;
2444 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002445 {
cristy7b8ed292013-05-18 01:13:56 +00002446 value=GetImageOption(image_info,"jpeg:sampling-factor");
2447 if (value == (char *) NULL)
2448 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2449 if (value != (char *) NULL)
2450 {
2451 sampling_factor=value;
2452 if (image->debug != MagickFalse)
2453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2454 " Input sampling-factors=%s",sampling_factor);
2455 }
cristy3ed852e2009-09-05 21:47:34 +00002456 }
cristy7b8ed292013-05-18 01:13:56 +00002457 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002458 if (image_info->sampling_factor != (char *) NULL)
2459 sampling_factor=image_info->sampling_factor;
2460 if (sampling_factor == (const char *) NULL)
2461 {
cristyc243f622015-02-21 17:20:34 +00002462 if (quality >= 90)
cristy3ed852e2009-09-05 21:47:34 +00002463 for (i=0; i < MAX_COMPONENTS; i++)
2464 {
2465 jpeg_info.comp_info[i].h_samp_factor=1;
2466 jpeg_info.comp_info[i].v_samp_factor=1;
2467 }
2468 }
2469 else
2470 {
2471 char
2472 **factors;
2473
2474 GeometryInfo
2475 geometry_info;
2476
2477 MagickStatusType
2478 flags;
2479
2480 /*
2481 Set sampling factor.
2482 */
2483 i=0;
2484 factors=SamplingFactorToList(sampling_factor);
2485 if (factors != (char **) NULL)
2486 {
2487 for (i=0; i < MAX_COMPONENTS; i++)
2488 {
2489 if (factors[i] == (char *) NULL)
2490 break;
2491 flags=ParseGeometry(factors[i],&geometry_info);
2492 if ((flags & SigmaValue) == 0)
2493 geometry_info.sigma=geometry_info.rho;
2494 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2495 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2496 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2497 }
2498 factors=(char **) RelinquishMagickMemory(factors);
2499 }
2500 for ( ; i < MAX_COMPONENTS; i++)
2501 {
2502 jpeg_info.comp_info[i].h_samp_factor=1;
2503 jpeg_info.comp_info[i].v_samp_factor=1;
2504 }
2505 }
cristy092ec8d2013-04-26 13:46:22 +00002506 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002507 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002508 {
cristy1b58f252012-03-01 01:41:41 +00002509 QuantizationTable
2510 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002511
cristy1445b322012-02-19 18:57:30 +00002512 /*
cristy1b58f252012-03-01 01:41:41 +00002513 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002514 */
cristy1b58f252012-03-01 01:41:41 +00002515 table=GetQuantizationTable(option,"0",exception);
2516 if (table != (QuantizationTable *) NULL)
2517 {
cristyae453b72013-04-21 22:24:20 +00002518 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002519 jpeg_info.comp_info[i].quant_tbl_no=0;
2520 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2521 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002522 table=DestroyQuantizationTable(table);
2523 }
2524 table=GetQuantizationTable(option,"1",exception);
2525 if (table != (QuantizationTable *) NULL)
2526 {
cristyae453b72013-04-21 22:24:20 +00002527 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002528 jpeg_info.comp_info[i].quant_tbl_no=1;
2529 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2530 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002531 table=DestroyQuantizationTable(table);
2532 }
2533 table=GetQuantizationTable(option,"2",exception);
2534 if (table != (QuantizationTable *) NULL)
2535 {
cristyae453b72013-04-21 22:24:20 +00002536 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002537 jpeg_info.comp_info[i].quant_tbl_no=2;
2538 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2539 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002540 table=DestroyQuantizationTable(table);
2541 }
2542 table=GetQuantizationTable(option,"3",exception);
2543 if (table != (QuantizationTable *) NULL)
2544 {
cristyae453b72013-04-21 22:24:20 +00002545 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002546 jpeg_info.comp_info[i].quant_tbl_no=3;
2547 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2548 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002549 table=DestroyQuantizationTable(table);
2550 }
cristy1445b322012-02-19 18:57:30 +00002551 }
cristy9bb7c842014-06-17 23:42:24 +00002552 jpeg_start_compress(&jpeg_info,TRUE);
cristy3ed852e2009-09-05 21:47:34 +00002553 if (image->debug != MagickFalse)
2554 {
2555 if (image->storage_class == PseudoClass)
2556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2557 "Storage class: PseudoClass");
2558 else
2559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2560 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2562 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002563 if (image->colors != 0)
2564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002565 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002566 else
2567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2568 "Number of colors: unspecified");
2569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2570 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2571 switch (image->colorspace)
2572 {
2573 case CMYKColorspace:
2574 {
2575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2576 "Storage class: DirectClass");
2577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2578 "Colorspace: CMYK");
2579 break;
2580 }
2581 case YCbCrColorspace:
2582 case Rec601YCbCrColorspace:
2583 case Rec709YCbCrColorspace:
2584 {
2585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2586 "Colorspace: YCbCr");
2587 break;
2588 }
2589 default:
2590 break;
2591 }
2592 switch (image->colorspace)
2593 {
2594 case CMYKColorspace:
2595 {
2596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2597 "Colorspace: CMYK");
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2600 jpeg_info.comp_info[0].h_samp_factor,
2601 jpeg_info.comp_info[0].v_samp_factor,
2602 jpeg_info.comp_info[1].h_samp_factor,
2603 jpeg_info.comp_info[1].v_samp_factor,
2604 jpeg_info.comp_info[2].h_samp_factor,
2605 jpeg_info.comp_info[2].v_samp_factor,
2606 jpeg_info.comp_info[3].h_samp_factor,
2607 jpeg_info.comp_info[3].v_samp_factor);
2608 break;
2609 }
2610 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002611 {
2612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2613 "Colorspace: GRAY");
2614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2616 jpeg_info.comp_info[0].v_samp_factor);
2617 break;
2618 }
cristyed8d7852013-08-01 22:44:22 +00002619 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002620 case RGBColorspace:
2621 {
2622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002623 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2625 "Sampling factors: %dx%d,%dx%d,%dx%d",
2626 jpeg_info.comp_info[0].h_samp_factor,
2627 jpeg_info.comp_info[0].v_samp_factor,
2628 jpeg_info.comp_info[1].h_samp_factor,
2629 jpeg_info.comp_info[1].v_samp_factor,
2630 jpeg_info.comp_info[2].h_samp_factor,
2631 jpeg_info.comp_info[2].v_samp_factor);
2632 break;
2633 }
2634 case YCbCrColorspace:
2635 case Rec601YCbCrColorspace:
2636 case Rec709YCbCrColorspace:
2637 {
2638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2639 "Colorspace: YCbCr");
2640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2641 "Sampling factors: %dx%d,%dx%d,%dx%d",
2642 jpeg_info.comp_info[0].h_samp_factor,
2643 jpeg_info.comp_info[0].v_samp_factor,
2644 jpeg_info.comp_info[1].h_samp_factor,
2645 jpeg_info.comp_info[1].v_samp_factor,
2646 jpeg_info.comp_info[2].h_samp_factor,
2647 jpeg_info.comp_info[2].v_samp_factor);
2648 break;
2649 }
2650 default:
2651 {
2652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2653 image->colorspace);
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2656 jpeg_info.comp_info[0].h_samp_factor,
2657 jpeg_info.comp_info[0].v_samp_factor,
2658 jpeg_info.comp_info[1].h_samp_factor,
2659 jpeg_info.comp_info[1].v_samp_factor,
2660 jpeg_info.comp_info[2].h_samp_factor,
2661 jpeg_info.comp_info[2].v_samp_factor,
2662 jpeg_info.comp_info[3].h_samp_factor,
2663 jpeg_info.comp_info[3].v_samp_factor);
2664 break;
2665 }
2666 }
2667 }
2668 /*
2669 Write JPEG profiles.
2670 */
cristyd15e6592011-10-15 00:13:06 +00002671 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002672 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002673 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002674 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2675 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2676 if (image->profiles != (void *) NULL)
2677 WriteProfile(&jpeg_info,image);
2678 /*
2679 Convert MIFF to JPEG raster pixels.
2680 */
cristy22646e22013-06-23 16:34:03 +00002681 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002682 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002683 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002684 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002685 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002686 if (setjmp(error_manager.error_recovery) != 0)
2687 {
2688 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002689 if (memory_info != (MemoryInfo *) NULL)
2690 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002691 (void) CloseBlob(image);
2692 return(MagickFalse);
2693 }
2694 scanline[0]=(JSAMPROW) jpeg_pixels;
cristy477b9fd2013-12-03 16:52:31 +00002695 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy31db6c12013-12-04 15:17:30 +00002696 if (scale == 0)
2697 scale=1;
cristye90d7402010-03-14 18:21:29 +00002698 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002699 {
cristy3ed852e2009-09-05 21:47:34 +00002700 if ((jpeg_info.in_color_space == JCS_RGB) ||
2701 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002702 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002703 {
cristy4c08aed2011-07-01 19:47:50 +00002704 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002705 *p;
2706
cristybb503372010-05-27 20:51:26 +00002707 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002708 x;
2709
cristy1e178e72011-08-28 19:44:34 +00002710 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002711 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002712 break;
2713 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002714 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002715 {
cristy4c08aed2011-07-01 19:47:50 +00002716 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2717 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2718 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002719 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002720 }
2721 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002722 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2723 image->rows);
2724 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002725 break;
2726 }
2727 else
cristye90d7402010-03-14 18:21:29 +00002728 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002729 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002730 {
cristyd0323222013-04-07 16:13:21 +00002731 register const Quantum
2732 *p;
2733
2734 register ssize_t
2735 x;
2736
2737 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2738 if (p == (const Quantum *) NULL)
2739 break;
2740 q=jpeg_pixels;
2741 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002742 {
cristyd0323222013-04-07 16:13:21 +00002743 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2744 image,p)));
2745 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002746 }
cristyd0323222013-04-07 16:13:21 +00002747 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2748 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2749 image->rows);
2750 if (status == MagickFalse)
2751 break;
2752 }
cristye90d7402010-03-14 18:21:29 +00002753 else
cristybb503372010-05-27 20:51:26 +00002754 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002755 {
cristy4c08aed2011-07-01 19:47:50 +00002756 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002757 *p;
2758
cristybb503372010-05-27 20:51:26 +00002759 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002760 x;
2761
cristy1e178e72011-08-28 19:44:34 +00002762 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002763 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002764 break;
2765 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002766 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002767 {
2768 /*
2769 Convert DirectClass packets to contiguous CMYK scanlines.
2770 */
2771 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002772 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002773 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002774 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002775 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002776 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002777 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002778 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002779 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002780 }
2781 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002782 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2783 image->rows);
2784 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002785 break;
2786 }
2787 }
2788 else
2789 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002790 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002791 {
cristy4c08aed2011-07-01 19:47:50 +00002792 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002793 *p;
2794
cristybb503372010-05-27 20:51:26 +00002795 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002796 x;
2797
cristy1e178e72011-08-28 19:44:34 +00002798 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002799 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002800 break;
2801 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002802 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002803 {
cristy217ddd62013-12-06 23:27:09 +00002804 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2805 p)))/scale);
cristyed231572011-07-14 02:18:59 +00002806 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002807 }
2808 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002809 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2810 image->rows);
2811 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002812 break;
2813 }
2814 else
2815 if ((jpeg_info.in_color_space == JCS_RGB) ||
2816 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002817 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002818 {
cristy4c08aed2011-07-01 19:47:50 +00002819 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002820 *p;
2821
cristybb503372010-05-27 20:51:26 +00002822 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002823 x;
2824
cristy1e178e72011-08-28 19:44:34 +00002825 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002826 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002827 break;
2828 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002829 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002830 {
cristy5328a4c2013-12-03 11:32:13 +00002831 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2832 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2833 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002834 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002835 }
2836 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002837 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002838 image->rows);
cristye90d7402010-03-14 18:21:29 +00002839 if (status == MagickFalse)
2840 break;
2841 }
2842 else
cristybb503372010-05-27 20:51:26 +00002843 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002844 {
cristy4c08aed2011-07-01 19:47:50 +00002845 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002846 *p;
2847
cristybb503372010-05-27 20:51:26 +00002848 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002849 x;
2850
cristy1e178e72011-08-28 19:44:34 +00002851 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002852 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002853 break;
2854 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002855 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002856 {
2857 /*
2858 Convert DirectClass packets to contiguous CMYK scanlines.
2859 */
cristy217ddd62013-12-06 23:27:09 +00002860 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2861 image,p))/scale);
2862 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2863 image,p))/scale);
2864 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2865 image,p))/scale);
2866 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2867 image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002868 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002869 }
2870 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002871 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002872 image->rows);
cristye90d7402010-03-14 18:21:29 +00002873 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002874 break;
2875 }
cristybb503372010-05-27 20:51:26 +00002876 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002877 jpeg_finish_compress(&jpeg_info);
2878 /*
2879 Relinquish resources.
2880 */
2881 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002882 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002883 (void) CloseBlob(image);
2884 return(MagickTrue);
2885}
2886#endif