blob: df06543917795f0cbbc498cc07b39f9609cd80af [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"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
cristy191ba5c2014-03-16 21:26:40 +000078#include "MagickCore/semaphore.h"
cristy4c08aed2011-07-01 19:47:50 +000079#include "MagickCore/splay-tree.h"
80#include "MagickCore/static.h"
81#include "MagickCore/string_.h"
82#include "MagickCore/string-private.h"
cristye40005d2012-03-23 12:18:45 +000083#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000084#include "MagickCore/utility.h"
cristy1b58f252012-03-01 01:41:41 +000085#include "MagickCore/xml-tree.h"
86#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000087#include <setjmp.h>
88#if defined(MAGICKCORE_JPEG_DELEGATE)
89#define JPEG_INTERNAL_OPTIONS
cristy07a3cca2012-12-10 13:09:10 +000090#if defined(__MINGW32__) || defined(__MINGW64__)
cristy3ed852e2009-09-05 21:47:34 +000091# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
92#endif
93#undef HAVE_STDLIB_H
94#include "jpeglib.h"
95#include "jerror.h"
96#endif
dirk29dd80e2013-10-31 23:11:11 +000097
98
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
dirk29dd80e2013-10-31 23:11:11 +0000107
108
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Typedef declarations.
111*/
112#if defined(MAGICKCORE_JPEG_DELEGATE)
113typedef struct _DestinationManager
114{
115 struct jpeg_destination_mgr
116 manager;
117
118 Image
119 *image;
120
121 JOCTET
122 *buffer;
123} DestinationManager;
124
125typedef struct _ErrorManager
126{
cristy018f07f2011-09-04 21:15:19 +0000127 ExceptionInfo
128 *exception;
129
cristy3ed852e2009-09-05 21:47:34 +0000130 Image
131 *image;
132
cristyd28b1dd2011-05-14 20:30:38 +0000133 MagickBooleanType
134 finished;
135
cristyf551aee2012-09-27 21:45:23 +0000136 StringInfo
137 *profile;
138
cristy3ed852e2009-09-05 21:47:34 +0000139 jmp_buf
140 error_recovery;
141} ErrorManager;
142
143typedef struct _SourceManager
144{
145 struct jpeg_source_mgr
146 manager;
147
148 Image
149 *image;
150
151 JOCTET
152 *buffer;
153
154 boolean
155 start_of_blob;
156} SourceManager;
157#endif
cristy1b58f252012-03-01 01:41:41 +0000158
159typedef struct _QuantizationTable
160{
161 char
162 *slot,
163 *description;
164
165 size_t
166 width,
167 height;
168
cristy043f3f32012-03-02 17:37:28 +0000169 double
170 divisor;
171
cristy1b58f252012-03-01 01:41:41 +0000172 unsigned int
cristy1b58f252012-03-01 01:41:41 +0000173 *levels;
174} QuantizationTable;
dirk29dd80e2013-10-31 23:11:11 +0000175
176
cristy3ed852e2009-09-05 21:47:34 +0000177/*
178 Forward declarations.
179*/
180#if defined(MAGICKCORE_JPEG_DELEGATE)
181static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000182 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000183#endif
dirk29dd80e2013-10-31 23:11:11 +0000184
185
cristy3ed852e2009-09-05 21:47:34 +0000186/*
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188% %
189% %
190% %
191% I s J P E G %
192% %
193% %
194% %
195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196%
197% IsJPEG() returns MagickTrue if the image format type, identified by the
198% magick string, is JPEG.
199%
200% The format of the IsJPEG method is:
201%
202% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
203%
204% A description of each parameter follows:
205%
206% o magick: compare image format pattern against these bytes.
207%
208% o length: Specifies the length of the magick string.
209%
210*/
211static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
212{
213 if (length < 3)
214 return(MagickFalse);
215 if (memcmp(magick,"\377\330\377",3) == 0)
216 return(MagickTrue);
217 return(MagickFalse);
218}
dirk29dd80e2013-10-31 23:11:11 +0000219
220
cristy3ed852e2009-09-05 21:47:34 +0000221#if defined(MAGICKCORE_JPEG_DELEGATE)
222/*
223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224% %
225% %
226% %
227% R e a d J P E G I m a g e %
228% %
229% %
230% %
231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232%
233% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
234% the memory necessary for the new Image structure and returns a pointer to
235% the new image.
236%
237% The format of the ReadJPEGImage method is:
238%
239% Image *ReadJPEGImage(const ImageInfo *image_info,
240% ExceptionInfo *exception)
241%
242% A description of each parameter follows:
243%
244% o image_info: the image info.
245%
246% o exception: return any errors or warnings in this structure.
247%
248*/
249
cristy3ed852e2009-09-05 21:47:34 +0000250static boolean FillInputBuffer(j_decompress_ptr cinfo)
251{
252 SourceManager
253 *source;
254
255 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000256 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
257 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000258 if (source->manager.bytes_in_buffer == 0)
259 {
cristy453b8202012-06-07 22:57:52 +0000260 if (source->start_of_blob != FALSE)
cristy3ed852e2009-09-05 21:47:34 +0000261 ERREXIT(cinfo,JERR_INPUT_EMPTY);
262 WARNMS(cinfo,JWRN_JPEG_EOF);
263 source->buffer[0]=(JOCTET) 0xff;
264 source->buffer[1]=(JOCTET) JPEG_EOI;
265 source->manager.bytes_in_buffer=2;
266 }
267 source->manager.next_input_byte=source->buffer;
268 source->start_of_blob=FALSE;
269 return(TRUE);
270}
271
272static int GetCharacter(j_decompress_ptr jpeg_info)
273{
274 if (jpeg_info->src->bytes_in_buffer == 0)
275 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
276 jpeg_info->src->bytes_in_buffer--;
277 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
278}
279
280static void InitializeSource(j_decompress_ptr cinfo)
281{
282 SourceManager
283 *source;
284
285 source=(SourceManager *) cinfo->src;
286 source->start_of_blob=TRUE;
287}
288
cristye8dd1302009-11-11 02:45:03 +0000289static MagickBooleanType IsITUFaxImage(const Image *image)
290{
291 const StringInfo
292 *profile;
293
294 const unsigned char
295 *datum;
296
cristyace6aa42009-11-11 03:17:33 +0000297 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000298 if (profile == (const StringInfo *) NULL)
299 return(MagickFalse);
300 if (GetStringInfoLength(profile) < 5)
301 return(MagickFalse);
302 datum=GetStringInfoDatum(profile);
303 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
304 (datum[3] == 0x41) && (datum[4] == 0x58))
305 return(MagickTrue);
306 return(MagickFalse);
307}
308
cristy437c3932011-10-12 18:03:46 +0000309static void JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000310{
cristyd28b1dd2011-05-14 20:30:38 +0000311 char
312 message[JMSG_LENGTH_MAX];
313
cristy3ed852e2009-09-05 21:47:34 +0000314 ErrorManager
315 *error_manager;
316
cristyc82a27b2011-10-21 01:07:16 +0000317 ExceptionInfo
318 *exception;
319
cristyd28b1dd2011-05-14 20:30:38 +0000320 Image
321 *image;
322
323 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000324 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000325 image=error_manager->image;
cristyc82a27b2011-10-21 01:07:16 +0000326 exception=error_manager->exception;
cristy86f33542011-05-21 22:58:33 +0000327 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000328 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
330 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000331 if (error_manager->finished != MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000332 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
333 (char *) message,"`%s'",image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000334 else
cristyc82a27b2011-10-21 01:07:16 +0000335 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
336 (char *) message,"`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000337 longjmp(error_manager->error_recovery,1);
338}
339
cristyd28b1dd2011-05-14 20:30:38 +0000340static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
341{
cristyf162e012012-03-28 12:54:33 +0000342#define JPEGExcessiveWarnings 1000
343
cristyd28b1dd2011-05-14 20:30:38 +0000344 char
345 message[JMSG_LENGTH_MAX];
346
347 ErrorManager
348 *error_manager;
349
cristy018f07f2011-09-04 21:15:19 +0000350 ExceptionInfo
351 *exception;
352
cristyd28b1dd2011-05-14 20:30:38 +0000353 Image
354 *image;
355
356 *message='\0';
357 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000358 exception=error_manager->exception;
cristyd28b1dd2011-05-14 20:30:38 +0000359 image=error_manager->image;
360 if (level < 0)
361 {
362 /*
363 Process warning message.
364 */
365 (jpeg_info->err->format_message)(jpeg_info,message);
cristyf162e012012-03-28 12:54:33 +0000366 if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
367 JPEGErrorHandler(jpeg_info);
cristy8043fb32012-06-28 16:14:12 +0000368 ThrowBinaryException(CorruptImageWarning,(char *) message,
369 image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000370 }
371 else
372 if ((image->debug != MagickFalse) &&
373 (level >= jpeg_info->err->trace_level))
374 {
375 /*
376 Process trace message.
377 */
378 (jpeg_info->err->format_message)(jpeg_info,message);
379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
380 "[%s] JPEG Trace: \"%s\"",image->filename,message);
381 }
382 return(MagickTrue);
383}
384
cristy3ed852e2009-09-05 21:47:34 +0000385static boolean ReadComment(j_decompress_ptr jpeg_info)
386{
cristy3ed852e2009-09-05 21:47:34 +0000387 ErrorManager
388 *error_manager;
389
cristy018f07f2011-09-04 21:15:19 +0000390 ExceptionInfo
391 *exception;
392
cristy3ed852e2009-09-05 21:47:34 +0000393 Image
394 *image;
395
cristyf551aee2012-09-27 21:45:23 +0000396 register unsigned char
cristy3ed852e2009-09-05 21:47:34 +0000397 *p;
398
cristybb503372010-05-27 20:51:26 +0000399 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000400 i;
401
402 size_t
403 length;
404
cristyf551aee2012-09-27 21:45:23 +0000405 StringInfo
406 *comment;
407
cristy3ed852e2009-09-05 21:47:34 +0000408 /*
409 Determine length of comment.
410 */
411 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000412 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000413 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000414 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000415 length+=GetCharacter(jpeg_info);
416 length-=2;
417 if (length <= 0)
cristydf878012014-01-12 23:39:40 +0000418 return(TRUE);
cristyf551aee2012-09-27 21:45:23 +0000419 comment=BlobToStringInfo((const void *) NULL,length);
420 if (comment == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000421 {
422 (void) ThrowMagickException(exception,GetMagickModule(),
423 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
424 return(FALSE);
425 }
cristy3ed852e2009-09-05 21:47:34 +0000426 /*
427 Read comment.
428 */
cristyf551aee2012-09-27 21:45:23 +0000429 error_manager->profile=comment;
430 p=GetStringInfoDatum(comment);
431 for (i=0; i < (ssize_t) GetStringInfoLength(comment); i++)
432 *p++=(unsigned char) GetCharacter(jpeg_info);
cristy3ed852e2009-09-05 21:47:34 +0000433 *p='\0';
cristyf551aee2012-09-27 21:45:23 +0000434 error_manager->profile=NULL;
435 p=GetStringInfoDatum(comment);
436 (void) SetImageProperty(image,"comment",(const char *) p,exception);
437 comment=DestroyStringInfo(comment);
cristydf878012014-01-12 23:39:40 +0000438 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000439}
440
441static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
442{
443 char
444 magick[12];
445
446 ErrorManager
447 *error_manager;
448
cristy018f07f2011-09-04 21:15:19 +0000449 ExceptionInfo
450 *exception;
451
cristy3ed852e2009-09-05 21:47:34 +0000452 Image
453 *image;
454
455 MagickBooleanType
456 status;
457
cristybb503372010-05-27 20:51:26 +0000458 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000459 i;
460
461 register unsigned char
462 *p;
463
464 size_t
465 length;
466
467 StringInfo
468 *icc_profile,
469 *profile;
470
471 /*
472 Read color profile.
473 */
cristybb503372010-05-27 20:51:26 +0000474 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000475 length+=(size_t) GetCharacter(jpeg_info);
476 length-=2;
477 if (length <= 14)
478 {
479 while (length-- > 0)
480 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000481 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000482 }
483 for (i=0; i < 12; i++)
484 magick[i]=(char) GetCharacter(jpeg_info);
485 if (LocaleCompare(magick,ICC_PROFILE) != 0)
486 {
487 /*
488 Not a ICC profile, return.
489 */
cristybb503372010-05-27 20:51:26 +0000490 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000491 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000492 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000493 }
494 (void) GetCharacter(jpeg_info); /* id */
495 (void) GetCharacter(jpeg_info); /* markers */
496 length-=14;
497 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000498 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000499 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000500 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000501 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000502 {
503 (void) ThrowMagickException(exception,GetMagickModule(),
504 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
505 return(FALSE);
506 }
cristyf551aee2012-09-27 21:45:23 +0000507 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000508 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000509 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000510 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000511 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000512 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
513 if (icc_profile != (StringInfo *) NULL)
514 {
515 ConcatenateStringInfo(icc_profile,profile);
516 profile=DestroyStringInfo(profile);
517 }
518 else
519 {
cristyd15e6592011-10-15 00:13:06 +0000520 status=SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000521 profile=DestroyStringInfo(profile);
522 if (status == MagickFalse)
523 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
524 image->filename);
525 }
526 if (image->debug != MagickFalse)
527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000528 "Profile: ICC, %.20g bytes",(double) length);
cristydf878012014-01-12 23:39:40 +0000529 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000530}
531
532static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
533{
534 char
535 magick[MaxTextExtent];
536
537 ErrorManager
538 *error_manager;
539
cristy018f07f2011-09-04 21:15:19 +0000540 ExceptionInfo
541 *exception;
542
cristy3ed852e2009-09-05 21:47:34 +0000543 Image
544 *image;
545
546 MagickBooleanType
547 status;
548
cristybb503372010-05-27 20:51:26 +0000549 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000550 i;
551
552 register unsigned char
553 *p;
554
555 size_t
556 length;
557
558 StringInfo
559 *iptc_profile,
560 *profile;
561
562 /*
563 Determine length of binary data stored here.
564 */
cristybb503372010-05-27 20:51:26 +0000565 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000566 length+=(size_t) GetCharacter(jpeg_info);
567 length-=2;
568 if (length <= 14)
569 {
570 while (length-- > 0)
571 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000572 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000573 }
574 /*
575 Validate that this was written as a Photoshop resource format slug.
576 */
577 for (i=0; i < 10; i++)
578 magick[i]=(char) GetCharacter(jpeg_info);
579 magick[10]='\0';
cristy4d0ca342014-05-01 00:42:09 +0000580 length-=10;
cristy3ed852e2009-09-05 21:47:34 +0000581 if (length <= 10)
cristydf878012014-01-12 23:39:40 +0000582 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000583 if (LocaleCompare(magick,"Photoshop ") != 0)
584 {
585 /*
586 Not a IPTC profile, return.
587 */
cristybb503372010-05-27 20:51:26 +0000588 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000589 (void) GetCharacter(jpeg_info);
cristydf878012014-01-12 23:39:40 +0000590 return(TRUE);
cristy3ed852e2009-09-05 21:47:34 +0000591 }
592 /*
593 Remove the version number.
594 */
595 for (i=0; i < 4; i++)
596 (void) GetCharacter(jpeg_info);
597 if (length <= 4)
598 return(MagickTrue);
599 length-=4;
600 if (length == 0)
601 return(MagickTrue);
602 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000603 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000604 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000605 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000606 if (profile == (StringInfo *) NULL)
cristydf878012014-01-12 23:39:40 +0000607 {
608 (void) ThrowMagickException(exception,GetMagickModule(),
609 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
610 return(FALSE);
611 }
cristyf551aee2012-09-27 21:45:23 +0000612 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000613 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000614 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000615 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000616 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000617 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
618 if (iptc_profile != (StringInfo *) NULL)
619 {
620 ConcatenateStringInfo(iptc_profile,profile);
621 profile=DestroyStringInfo(profile);
622 }
623 else
624 {
cristyd15e6592011-10-15 00:13:06 +0000625 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000626 profile=DestroyStringInfo(profile);
627 if (status == MagickFalse)
628 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
629 image->filename);
630 }
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
640 name[MaxTextExtent];
641
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;
cristyb51dff52011-05-19 16:55:47 +0000681 (void) FormatLocaleString(name,MaxTextExtent,"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))
701 (void) CopyMagickString(name,"exif",MaxTextExtent);
702 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)));
719 (void) CopyMagickString(name,"xmp",MaxTextExtent);
720 }
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
933 sampling_factor[MaxTextExtent];
934
935 switch (jpeg_info->out_color_space)
936 {
937 case JCS_CMYK:
938 {
939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000940 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
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");
cristyb51dff52011-05-19 16:55:47 +0000955 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%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");
cristyb51dff52011-05-19 16:55:47 +0000963 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
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);
cristyb51dff52011-05-19 16:55:47 +0000976 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
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
998 value[MaxTextExtent];
999
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);
1050 assert(image_info->signature == MagickSignature);
1051 if (image_info->debug != MagickFalse)
1052 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1053 image_info->filename);
1054 assert(exception != (ExceptionInfo *) NULL);
1055 assert(exception->signature == MagickSignature);
1056 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);
1092 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
1093 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1094 for (i=1; i < 16; i++)
1095 if ((i != 2) && (i != 13) && (i != 14))
1096 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristydf878012014-01-12 23:39:40 +00001097 i=(ssize_t) jpeg_read_header(&jpeg_info,TRUE);
cristy53215c82009-09-19 16:32:36 +00001098 if ((image_info->colorspace == YCbCrColorspace) ||
1099 (image_info->colorspace == Rec601YCbCrColorspace) ||
1100 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001101 jpeg_info.out_color_space=JCS_YCbCr;
1102 /*
1103 Set image resolution.
1104 */
1105 units=0;
1106 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1107 (jpeg_info.Y_density != 1))
1108 {
cristy2a11bef2011-10-28 18:33:11 +00001109 image->resolution.x=(double) jpeg_info.X_density;
1110 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001111 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001112 }
1113 if (units == 1)
1114 image->units=PixelsPerInchResolution;
1115 if (units == 2)
1116 image->units=PixelsPerCentimeterResolution;
1117 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy092ec8d2013-04-26 13:46:22 +00001118 option=GetImageOption(image_info,"jpeg:size");
cristy11151212009-09-14 13:10:15 +00001119 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001120 {
1121 double
1122 scale_factor;
1123
cristycaec74e2009-09-14 02:20:04 +00001124 GeometryInfo
1125 geometry_info;
1126
cristy0adb4f92009-11-28 18:08:51 +00001127 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001128 flags;
1129
cristy3ed852e2009-09-05 21:47:34 +00001130 /*
cristycaec74e2009-09-14 02:20:04 +00001131 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001132 */
cristy11151212009-09-14 13:10:15 +00001133 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001134 if ((flags & SigmaValue) == 0)
1135 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001136 jpeg_calc_output_dimensions(&jpeg_info);
1137 image->magick_columns=jpeg_info.output_width;
1138 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001139 scale_factor=1.0;
1140 if (geometry_info.rho != 0.0)
1141 scale_factor=jpeg_info.output_width/geometry_info.rho;
1142 if ((geometry_info.sigma != 0.0) &&
1143 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1144 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001145 jpeg_info.scale_num=1U;
1146 jpeg_info.scale_denom=(unsigned int) scale_factor;
1147 jpeg_calc_output_dimensions(&jpeg_info);
1148 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1150 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001151 }
cristy3ed852e2009-09-05 21:47:34 +00001152#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1153#if defined(D_LOSSLESS_SUPPORTED)
1154 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1155 JPEGInterlace : NoInterlace;
1156 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1157 LosslessJPEGCompression : JPEGCompression;
1158 if (jpeg_info.data_precision > 8)
1159 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1160 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1161 image->filename);
1162 if (jpeg_info.data_precision == 16)
1163 jpeg_info.data_precision=12;
1164#else
1165 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1166 NoInterlace;
1167 image->compression=JPEGCompression;
1168#endif
1169#else
1170 image->compression=JPEGCompression;
1171 image->interlace=JPEGInterlace;
1172#endif
cristy092ec8d2013-04-26 13:46:22 +00001173 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001174 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001175 {
cristyb4bb39c2012-02-21 18:45:54 +00001176 /*
1177 Let the JPEG library quantize the image.
1178 */
cristydf878012014-01-12 23:39:40 +00001179 jpeg_info.quantize_colors=TRUE;
anthony41906552011-10-07 12:15:27 +00001180 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001181 }
cristy092ec8d2013-04-26 13:46:22 +00001182 option=GetImageOption(image_info,"jpeg:block-smoothing");
cristy0d737182013-04-04 00:22:57 +00001183 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001184 jpeg_info.do_block_smoothing=IsStringTrue(option) != MagickFalse ? TRUE :
1185 FALSE;
cristyb4bb39c2012-02-21 18:45:54 +00001186 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00001187 option=GetImageOption(image_info,"jpeg:dct-method");
cristy787d4352010-03-06 13:55:58 +00001188 if (option != (const char *) NULL)
1189 switch (*option)
1190 {
1191 case 'D':
1192 case 'd':
1193 {
1194 if (LocaleCompare(option,"default") == 0)
1195 jpeg_info.dct_method=JDCT_DEFAULT;
1196 break;
1197 }
1198 case 'F':
1199 case 'f':
1200 {
1201 if (LocaleCompare(option,"fastest") == 0)
1202 jpeg_info.dct_method=JDCT_FASTEST;
1203 if (LocaleCompare(option,"float") == 0)
1204 jpeg_info.dct_method=JDCT_FLOAT;
1205 break;
1206 }
1207 case 'I':
1208 case 'i':
1209 {
1210 if (LocaleCompare(option,"ifast") == 0)
1211 jpeg_info.dct_method=JDCT_IFAST;
1212 if (LocaleCompare(option,"islow") == 0)
1213 jpeg_info.dct_method=JDCT_ISLOW;
1214 break;
1215 }
1216 }
cristy092ec8d2013-04-26 13:46:22 +00001217 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
cristy0d737182013-04-04 00:22:57 +00001218 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00001219 jpeg_info.do_fancy_upsampling=IsStringTrue(option) != MagickFalse ? TRUE :
1220 FALSE;
cristy3ed852e2009-09-05 21:47:34 +00001221 (void) jpeg_start_decompress(&jpeg_info);
1222 image->columns=jpeg_info.output_width;
1223 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001224 image->depth=(size_t) jpeg_info.data_precision;
cristy93ff2cc2012-05-13 21:03:53 +00001225 switch (jpeg_info.out_color_space)
1226 {
1227 case JCS_RGB:
1228 default:
1229 {
cristyf551aee2012-09-27 21:45:23 +00001230 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001231 break;
1232 }
1233 case JCS_GRAYSCALE:
1234 {
cristy1a45be72013-04-07 00:16:24 +00001235 (void) SetImageColorspace(image,GRAYColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001236 break;
1237 }
1238 case JCS_YCbCr:
1239 {
cristyf551aee2012-09-27 21:45:23 +00001240 (void) SetImageColorspace(image,YCbCrColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001241 break;
1242 }
1243 case JCS_CMYK:
1244 {
cristyf551aee2012-09-27 21:45:23 +00001245 (void) SetImageColorspace(image,CMYKColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001246 break;
1247 }
1248 }
cristy36fc5502012-05-22 11:32:23 +00001249 if (IsITUFaxImage(image) != MagickFalse)
1250 {
cristyf551aee2012-09-27 21:45:23 +00001251 (void) SetImageColorspace(image,LabColorspace,exception);
cristy36fc5502012-05-22 11:32:23 +00001252 jpeg_info.out_color_space=JCS_YCbCr;
1253 }
cristy092ec8d2013-04-26 13:46:22 +00001254 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001255 if (option != (const char *) NULL)
1256 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1257 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001258 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1259 if ((jpeg_info.output_components == 1) &&
1260 (jpeg_info.quantize_colors == MagickFalse))
1261 {
cristybb503372010-05-27 20:51:26 +00001262 size_t
cristy3ed852e2009-09-05 21:47:34 +00001263 colors;
1264
cristybb503372010-05-27 20:51:26 +00001265 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001266 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001267 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1268 }
1269 if (image->debug != MagickFalse)
1270 {
1271 if (image->interlace != NoInterlace)
1272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1273 "Interlace: progressive");
1274 else
1275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1276 "Interlace: nonprogressive");
1277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1278 (int) jpeg_info.data_precision);
1279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1280 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1281 }
dirk881355a2013-08-19 19:56:50 +00001282 JPEGSetImageQuality(&jpeg_info,image,exception);
cristyd15e6592011-10-15 00:13:06 +00001283 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristyb51dff52011-05-19 16:55:47 +00001284 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001285 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001286 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001287 if (image_info->ping != MagickFalse)
1288 {
1289 jpeg_destroy_decompress(&jpeg_info);
1290 (void) CloseBlob(image);
1291 return(GetFirstImageInList(image));
1292 }
cristy22646e22013-06-23 16:34:03 +00001293 memory_info=AcquireVirtualMemory((size_t) image->columns,
1294 jpeg_info.output_components*sizeof(*jpeg_pixels));
1295 if (memory_info == (MemoryInfo *) NULL)
cristyee1bdaa2013-07-16 16:45:03 +00001296 {
1297 jpeg_destroy_decompress(&jpeg_info);
1298 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1299 }
cristy22646e22013-06-23 16:34:03 +00001300 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001301 /*
1302 Convert JPEG pixels to pixel packets.
1303 */
1304 if (setjmp(error_manager.error_recovery) != 0)
1305 {
cristy22646e22013-06-23 16:34:03 +00001306 if (memory_info != (MemoryInfo *) NULL)
1307 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001308 jpeg_destroy_decompress(&jpeg_info);
1309 (void) CloseBlob(image);
1310 number_pixels=(MagickSizeType) image->columns*image->rows;
1311 if (number_pixels != 0)
1312 return(GetFirstImageInList(image));
1313 return(DestroyImage(image));
1314 }
1315 if (jpeg_info.quantize_colors != MagickFalse)
1316 {
cristybb503372010-05-27 20:51:26 +00001317 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001318 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001319 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001320 {
cristy1b58f252012-03-01 01:41:41 +00001321 image->colormap[i].red=(double) ScaleCharToQuantum(
1322 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001323 image->colormap[i].green=image->colormap[i].red;
1324 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001325 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001326 }
1327 else
cristybb503372010-05-27 20:51:26 +00001328 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001329 {
cristy1b58f252012-03-01 01:41:41 +00001330 image->colormap[i].red=(double) ScaleCharToQuantum(
1331 jpeg_info.colormap[0][i]);
1332 image->colormap[i].green=(double) ScaleCharToQuantum(
1333 jpeg_info.colormap[1][i]);
1334 image->colormap[i].blue=(double) ScaleCharToQuantum(
1335 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001336 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001337 }
1338 }
1339 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001340 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001341 {
cristybb503372010-05-27 20:51:26 +00001342 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001343 x;
1344
cristy4c08aed2011-07-01 19:47:50 +00001345 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001346 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001347
1348 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1349 {
1350 (void) ThrowMagickException(exception,GetMagickModule(),
1351 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1352 continue;
1353 }
1354 p=jpeg_pixels;
1355 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001356 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001357 break;
cristy3ed852e2009-09-05 21:47:34 +00001358 if (jpeg_info.data_precision > 8)
1359 {
cristy5328a4c2013-12-03 11:32:13 +00001360 unsigned short
1361 scale;
1362
cristy477b9fd2013-12-03 16:52:31 +00001363 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy3ed852e2009-09-05 21:47:34 +00001364 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001365 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001366 {
cristybb503372010-05-27 20:51:26 +00001367 size_t
cristy3ed852e2009-09-05 21:47:34 +00001368 pixel;
1369
cristy5328a4c2013-12-03 11:32:13 +00001370 pixel=(size_t) (scale*GETJSAMPLE(*p));
cristyc82a27b2011-10-21 01:07:16 +00001371 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001372 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001373 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001374 p++;
cristyed231572011-07-14 02:18:59 +00001375 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001376 }
1377 else
1378 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001379 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001380 {
cristy217ddd62013-12-06 23:27:09 +00001381 SetPixelRed(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1382 q);
1383 SetPixelGreen(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1384 q);
1385 SetPixelBlue(image,ScaleShortToQuantum(scale*GETJSAMPLE(*p++)),
1386 q);
cristy4c08aed2011-07-01 19:47:50 +00001387 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001388 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001389 }
1390 else
cristybb503372010-05-27 20:51:26 +00001391 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001392 {
cristy217ddd62013-12-06 23:27:09 +00001393 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(scale*
1394 GETJSAMPLE(*p++)),q);
1395 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(scale*
1396 GETJSAMPLE(*p++)),q);
1397 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(scale*
1398 GETJSAMPLE(*p++)),q);
1399 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(scale*
1400 GETJSAMPLE(*p++)),q);
cristy4c08aed2011-07-01 19:47:50 +00001401 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001402 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001403 }
1404 }
1405 else
1406 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001407 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001408 {
cristyc82a27b2011-10-21 01:07:16 +00001409 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001410 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001411 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001412 p++;
cristyed231572011-07-14 02:18:59 +00001413 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001414 }
1415 else
1416 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001417 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001418 {
cristy4c08aed2011-07-01 19:47:50 +00001419 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1420 GETJSAMPLE(*p++)),q);
1421 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1422 GETJSAMPLE(*p++)),q);
1423 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1424 GETJSAMPLE(*p++)),q);
1425 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001426 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001427 }
1428 else
cristybb503372010-05-27 20:51:26 +00001429 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001430 {
cristy4c08aed2011-07-01 19:47:50 +00001431 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1432 (unsigned char) GETJSAMPLE(*p++)),q);
1433 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1434 (unsigned char) GETJSAMPLE(*p++)),q);
1435 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1436 (unsigned char) GETJSAMPLE(*p++)),q);
1437 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1438 (unsigned char) GETJSAMPLE(*p++)),q);
1439 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001440 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001441 }
1442 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1443 break;
cristy524222d2011-04-25 00:37:06 +00001444 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1445 image->rows);
1446 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001447 {
1448 jpeg_abort_decompress(&jpeg_info);
1449 break;
1450 }
cristy3ed852e2009-09-05 21:47:34 +00001451 }
cristyd28b1dd2011-05-14 20:30:38 +00001452 if (status != MagickFalse)
1453 {
1454 error_manager.finished=MagickTrue;
1455 if (setjmp(error_manager.error_recovery) == 0)
1456 (void) jpeg_finish_decompress(&jpeg_info);
1457 }
cristy3ed852e2009-09-05 21:47:34 +00001458 /*
1459 Free jpeg resources.
1460 */
cristy3ed852e2009-09-05 21:47:34 +00001461 jpeg_destroy_decompress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00001462 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001463 (void) CloseBlob(image);
1464 return(GetFirstImageInList(image));
1465}
1466#endif
dirk29dd80e2013-10-31 23:11:11 +00001467
1468
cristy3ed852e2009-09-05 21:47:34 +00001469/*
1470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1471% %
1472% %
1473% %
1474% R e g i s t e r J P E G I m a g e %
1475% %
1476% %
1477% %
1478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1479%
1480% RegisterJPEGImage() adds properties for the JPEG image format to
1481% the list of supported formats. The properties include the image format
1482% tag, a method to read and/or write the format, whether the format
1483% supports the saving of more than one frame to the same file or blob,
1484% whether the format supports native in-memory I/O, and a brief
1485% description of the format.
1486%
1487% The format of the RegisterJPEGImage method is:
1488%
cristybb503372010-05-27 20:51:26 +00001489% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001490%
1491*/
cristybb503372010-05-27 20:51:26 +00001492ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001493{
1494 char
1495 version[MaxTextExtent];
1496
1497 MagickInfo
1498 *entry;
1499
1500 static const char
cristy7138c592009-09-08 13:58:52 +00001501 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001502
1503 *version='\0';
1504#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001505 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001506#endif
1507 entry=SetMagickInfo("JPEG");
1508 entry->thread_support=NoThreadSupport;
1509#if defined(MAGICKCORE_JPEG_DELEGATE)
1510 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1511 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1512#endif
1513 entry->magick=(IsImageFormatHandler *) IsJPEG;
1514 entry->adjoin=MagickFalse;
1515 entry->description=ConstantString(description);
1516 if (*version != '\0')
1517 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001518 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001519 entry->module=ConstantString("JPEG");
1520 (void) RegisterMagickInfo(entry);
1521 entry=SetMagickInfo("JPG");
1522 entry->thread_support=NoThreadSupport;
1523#if defined(MAGICKCORE_JPEG_DELEGATE)
1524 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1525 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1526#endif
1527 entry->adjoin=MagickFalse;
1528 entry->description=ConstantString(description);
1529 if (*version != '\0')
1530 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001531 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001532 entry->module=ConstantString("JPEG");
1533 (void) RegisterMagickInfo(entry);
1534 entry=SetMagickInfo("PJPEG");
1535 entry->thread_support=NoThreadSupport;
1536#if defined(MAGICKCORE_JPEG_DELEGATE)
1537 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1538 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1539#endif
1540 entry->adjoin=MagickFalse;
1541 entry->description=ConstantString(description);
1542 if (*version != '\0')
1543 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001544 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001545 entry->module=ConstantString("JPEG");
1546 (void) RegisterMagickInfo(entry);
1547 return(MagickImageCoderSignature);
1548}
dirk29dd80e2013-10-31 23:11:11 +00001549
1550
cristy3ed852e2009-09-05 21:47:34 +00001551/*
1552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553% %
1554% %
1555% %
1556% U n r e g i s t e r J P E G I m a g e %
1557% %
1558% %
1559% %
1560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561%
1562% UnregisterJPEGImage() removes format registrations made by the
1563% JPEG module from the list of supported formats.
1564%
1565% The format of the UnregisterJPEGImage method is:
1566%
1567% UnregisterJPEGImage(void)
1568%
1569*/
1570ModuleExport void UnregisterJPEGImage(void)
1571{
1572 (void) UnregisterMagickInfo("PJPG");
1573 (void) UnregisterMagickInfo("JPEG");
1574 (void) UnregisterMagickInfo("JPG");
1575}
dirk29dd80e2013-10-31 23:11:11 +00001576
1577
cristy3ed852e2009-09-05 21:47:34 +00001578#if defined(MAGICKCORE_JPEG_DELEGATE)
1579/*
1580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1581% %
1582% %
1583% %
1584% W r i t e J P E G I m a g e %
1585% %
1586% %
1587% %
1588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1589%
1590% WriteJPEGImage() writes a JPEG image file and returns it. It
1591% allocates the memory necessary for the new Image structure and returns a
1592% pointer to the new image.
1593%
1594% The format of the WriteJPEGImage method is:
1595%
cristy91044972011-04-22 14:21:16 +00001596% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001597% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001598%
1599% A description of each parameter follows:
1600%
1601% o image_info: the image info.
1602%
1603% o jpeg_image: The image.
1604%
cristy1e178e72011-08-28 19:44:34 +00001605% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001606%
1607*/
1608
cristy1b58f252012-03-01 01:41:41 +00001609static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1610{
1611 assert(table != (QuantizationTable *) NULL);
1612 if (table->slot != (char *) NULL)
1613 table->slot=DestroyString(table->slot);
1614 if (table->description != (char *) NULL)
1615 table->description=DestroyString(table->description);
1616 if (table->levels != (unsigned int *) NULL)
1617 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1618 table=(QuantizationTable *) RelinquishMagickMemory(table);
1619 return(table);
1620}
1621
cristy3ed852e2009-09-05 21:47:34 +00001622static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1623{
1624 DestinationManager
1625 *destination;
1626
1627 destination=(DestinationManager *) cinfo->dest;
1628 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1629 MaxBufferExtent,destination->buffer);
1630 if (destination->manager.free_in_buffer != MaxBufferExtent)
1631 ERREXIT(cinfo,JERR_FILE_WRITE);
1632 destination->manager.next_output_byte=destination->buffer;
1633 return(TRUE);
1634}
1635
cristy1b58f252012-03-01 01:41:41 +00001636static QuantizationTable *GetQuantizationTable(const char *filename,
1637 const char *slot,ExceptionInfo *exception)
1638{
1639 char
1640 *p,
1641 *xml;
1642
1643 const char
1644 *attribute,
1645 *content;
1646
1647 double
1648 value;
1649
1650 register ssize_t
1651 i;
1652
1653 ssize_t
1654 j;
1655
1656 QuantizationTable
1657 *table;
1658
cristy1fcc8b62012-03-02 17:25:55 +00001659 size_t
1660 length;
1661
cristy1b58f252012-03-01 01:41:41 +00001662 XMLTreeInfo
1663 *description,
1664 *levels,
1665 *quantization_tables,
1666 *table_iterator;
1667
1668 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1669 "Loading quantization tables \"%s\" ...",filename);
1670 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001671 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001672 if (xml == (char *) NULL)
1673 return(table);
1674 quantization_tables=NewXMLTree(xml,exception);
1675 if (quantization_tables == (XMLTreeInfo *) NULL)
1676 {
1677 xml=DestroyString(xml);
1678 return(table);
1679 }
1680 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1681 table_iterator != (XMLTreeInfo *) NULL;
1682 table_iterator=GetNextXMLTreeTag(table_iterator))
1683 {
1684 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1685 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1686 break;
1687 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1688 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1689 break;
1690 }
1691 if (table_iterator == (XMLTreeInfo *) NULL)
1692 {
1693 xml=DestroyString(xml);
1694 return(table);
1695 }
1696 description=GetXMLTreeChild(table_iterator,"description");
1697 if (description == (XMLTreeInfo *) NULL)
1698 {
1699 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1700 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1701 quantization_tables=DestroyXMLTree(quantization_tables);
1702 xml=DestroyString(xml);
1703 return(table);
1704 }
1705 levels=GetXMLTreeChild(table_iterator,"levels");
1706 if (levels == (XMLTreeInfo *) NULL)
1707 {
1708 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1709 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1710 quantization_tables=DestroyXMLTree(quantization_tables);
1711 xml=DestroyString(xml);
1712 return(table);
1713 }
1714 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1715 if (table == (QuantizationTable *) NULL)
1716 ThrowFatalException(ResourceLimitFatalError,
1717 "UnableToAcquireQuantizationTable");
1718 table->slot=(char *) NULL;
1719 table->description=(char *) NULL;
1720 table->levels=(unsigned int *) NULL;
1721 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1722 if (attribute != (char *) NULL)
1723 table->slot=ConstantString(attribute);
1724 content=GetXMLTreeContent(description);
1725 if (content != (char *) NULL)
1726 table->description=ConstantString(content);
1727 attribute=GetXMLTreeAttribute(levels,"width");
1728 if (attribute == (char *) NULL)
1729 {
1730 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1731 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1732 quantization_tables=DestroyXMLTree(quantization_tables);
1733 table=DestroyQuantizationTable(table);
1734 xml=DestroyString(xml);
1735 return(table);
1736 }
1737 table->width=StringToUnsignedLong(attribute);
1738 if (table->width == 0)
1739 {
1740 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1741 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1742 quantization_tables=DestroyXMLTree(quantization_tables);
1743 table=DestroyQuantizationTable(table);
1744 xml=DestroyString(xml);
1745 return(table);
1746 }
1747 attribute=GetXMLTreeAttribute(levels,"height");
1748 if (attribute == (char *) NULL)
1749 {
1750 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1751 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1752 quantization_tables=DestroyXMLTree(quantization_tables);
1753 table=DestroyQuantizationTable(table);
1754 xml=DestroyString(xml);
1755 return(table);
1756 }
1757 table->height=StringToUnsignedLong(attribute);
1758 if (table->height == 0)
1759 {
1760 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1761 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1762 quantization_tables=DestroyXMLTree(quantization_tables);
1763 table=DestroyQuantizationTable(table);
1764 xml=DestroyString(xml);
1765 return(table);
1766 }
1767 attribute=GetXMLTreeAttribute(levels,"divisor");
1768 if (attribute == (char *) NULL)
1769 {
1770 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1771 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1772 quantization_tables=DestroyXMLTree(quantization_tables);
1773 table=DestroyQuantizationTable(table);
1774 xml=DestroyString(xml);
1775 return(table);
1776 }
cristy043f3f32012-03-02 17:37:28 +00001777 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1778 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001779 {
1780 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1781 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1782 quantization_tables=DestroyXMLTree(quantization_tables);
1783 table=DestroyQuantizationTable(table);
1784 xml=DestroyString(xml);
1785 return(table);
1786 }
1787 content=GetXMLTreeContent(levels);
1788 if (content == (char *) NULL)
1789 {
1790 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1791 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1792 quantization_tables=DestroyXMLTree(quantization_tables);
1793 table=DestroyQuantizationTable(table);
1794 xml=DestroyString(xml);
1795 return(table);
1796 }
cristy1fcc8b62012-03-02 17:25:55 +00001797 length=(size_t) table->width*table->height;
1798 if (length < 64)
1799 length=64;
cristy092006b2012-03-02 17:26:02 +00001800 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001801 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001802 if (table->levels == (unsigned int *) NULL)
1803 ThrowFatalException(ResourceLimitFatalError,
1804 "UnableToAcquireQuantizationTable");
1805 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1806 {
cristy043f3f32012-03-02 17:37:28 +00001807 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1808 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001809 while (isspace((int) ((unsigned char) *p)) != 0)
1810 p++;
1811 if (*p == ',')
1812 p++;
1813 content=p;
1814 }
cristy043f3f32012-03-02 17:37:28 +00001815 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001816 (void) value;
1817 if (p != content)
1818 {
1819 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1820 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1821 quantization_tables=DestroyXMLTree(quantization_tables);
1822 table=DestroyQuantizationTable(table);
1823 xml=DestroyString(xml);
1824 return(table);
1825 }
cristy043f3f32012-03-02 17:37:28 +00001826 for (j=i; j < 64; j++)
1827 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001828 quantization_tables=DestroyXMLTree(quantization_tables);
1829 xml=DestroyString(xml);
1830 return(table);
1831}
1832
cristy3ed852e2009-09-05 21:47:34 +00001833static void InitializeDestination(j_compress_ptr cinfo)
1834{
1835 DestinationManager
1836 *destination;
1837
1838 destination=(DestinationManager *) cinfo->dest;
1839 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1840 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1841 destination->manager.next_output_byte=destination->buffer;
1842 destination->manager.free_in_buffer=MaxBufferExtent;
1843}
1844
1845static inline size_t MagickMin(const size_t x,const size_t y)
1846{
1847 if (x < y)
1848 return(x);
1849 return(y);
1850}
1851
1852static void TerminateDestination(j_compress_ptr cinfo)
1853{
1854 DestinationManager
1855 *destination;
1856
1857 destination=(DestinationManager *) cinfo->dest;
1858 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1859 {
1860 ssize_t
1861 count;
1862
1863 count=WriteBlob(destination->image,MaxBufferExtent-
1864 destination->manager.free_in_buffer,destination->buffer);
1865 if (count != (ssize_t)
1866 (MaxBufferExtent-destination->manager.free_in_buffer))
1867 ERREXIT(cinfo,JERR_FILE_WRITE);
1868 }
1869}
1870
1871static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1872{
1873 const char
1874 *name;
1875
1876 const StringInfo
1877 *profile;
1878
1879 MagickBooleanType
1880 iptc;
1881
cristybb503372010-05-27 20:51:26 +00001882 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001883 i;
1884
1885 size_t
cristy524222d2011-04-25 00:37:06 +00001886 length,
1887 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001888
1889 StringInfo
1890 *custom_profile;
1891
cristy3ed852e2009-09-05 21:47:34 +00001892 /*
1893 Save image profile as a APP marker.
1894 */
1895 iptc=MagickFalse;
1896 custom_profile=AcquireStringInfo(65535L);
1897 ResetImageProfileIterator(image);
1898 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1899 {
cristy109e5572010-09-16 18:38:17 +00001900 register unsigned char
1901 *p;
1902
cristy3ed852e2009-09-05 21:47:34 +00001903 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001904 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001905 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001906 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001907 {
1908 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1909 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1910 (unsigned int) length);
1911 }
1912 if (LocaleCompare(name,"ICC") == 0)
1913 {
1914 register unsigned char
1915 *p;
1916
cristy8ba82ae2013-05-29 14:19:49 +00001917 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001918 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001919 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001920 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001921 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001922 {
1923 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001924 p[12]=(unsigned char) ((i/65519L)+1);
1925 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001926 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001927 length);
1928 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001929 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001930 }
1931 }
1932 if (((LocaleCompare(name,"IPTC") == 0) ||
1933 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1934 {
cristybb503372010-05-27 20:51:26 +00001935 size_t
cristy3ed852e2009-09-05 21:47:34 +00001936 roundup;
1937
1938 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001939 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001940 {
1941 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001942 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001943 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1944 {
1945 (void) memcpy(p,"Photoshop 3.0 ",14);
1946 tag_length=14;
1947 }
1948 else
1949 {
1950 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1951 tag_length=26;
1952 p[24]=(unsigned char) (length >> 8);
1953 p[25]=(unsigned char) (length & 0xff);
1954 }
1955 p[13]=0x00;
1956 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001957 if (roundup != 0)
1958 p[length+tag_length]='\0';
1959 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1960 custom_profile),(unsigned int) (length+tag_length+roundup));
1961 }
1962 }
1963 if (LocaleCompare(name,"XMP") == 0)
1964 {
1965 StringInfo
1966 *xmp_profile;
1967
1968 /*
1969 Add namespace to XMP profile.
1970 */
cristy0615f0e2011-10-12 11:36:46 +00001971 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy30841e62014-05-19 00:45:15 +00001972 if (xmp_profile != (StringInfo *) NULL)
cristy8418c7e2014-05-18 18:38:26 +00001973 {
1974 ConcatenateStringInfo(xmp_profile,profile);
1975 GetStringInfoDatum(xmp_profile)[28]='\0';
1976 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
1977 {
1978 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1979 jpeg_write_marker(jpeg_info,XML_MARKER,
1980 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1981 }
1982 xmp_profile=DestroyStringInfo(xmp_profile);
1983 }
cristy3ed852e2009-09-05 21:47:34 +00001984 }
cristye8c25f92010-06-03 00:53:06 +00001985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1986 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001987 name=GetNextImageProfile(image);
1988 }
1989 custom_profile=DestroyStringInfo(custom_profile);
1990}
1991
1992static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1993{
1994 DestinationManager
1995 *destination;
1996
1997 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1998 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1999 destination=(DestinationManager *) cinfo->dest;
2000 destination->manager.init_destination=InitializeDestination;
2001 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2002 destination->manager.term_destination=TerminateDestination;
2003 destination->image=image;
2004}
2005
2006static char **SamplingFactorToList(const char *text)
2007{
2008 char
2009 **textlist;
2010
2011 register char
2012 *q;
2013
2014 register const char
2015 *p;
2016
cristybb503372010-05-27 20:51:26 +00002017 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002018 i;
2019
cristybb503372010-05-27 20:51:26 +00002020 size_t
cristy3ed852e2009-09-05 21:47:34 +00002021 lines;
2022
2023 if (text == (char *) NULL)
2024 return((char **) NULL);
2025 /*
2026 Convert string to an ASCII list.
2027 */
2028 lines=1;
2029 for (p=text; *p != '\0'; p++)
2030 if (*p == ',')
2031 lines++;
2032 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
2033 sizeof(*textlist));
2034 if (textlist == (char **) NULL)
2035 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2036 p=text;
cristybb503372010-05-27 20:51:26 +00002037 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00002038 {
2039 for (q=(char *) p; *q != '\0'; q++)
2040 if (*q == ',')
2041 break;
2042 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
2043 sizeof(*textlist[i]));
2044 if (textlist[i] == (char *) NULL)
2045 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2046 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2047 if (*q == '\r')
2048 q++;
2049 p=q+1;
2050 }
2051 textlist[i]=(char *) NULL;
2052 return(textlist);
2053}
2054
2055static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002056 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002057{
2058 const char
2059 *option,
2060 *sampling_factor,
2061 *value;
2062
2063 ErrorManager
2064 error_manager;
2065
cristy21749e92012-02-18 02:29:21 +00002066 int
cristy7b8ed292013-05-18 01:13:56 +00002067 colorspace,
cristy21749e92012-02-18 02:29:21 +00002068 quality;
2069
cristy3ed852e2009-09-05 21:47:34 +00002070 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002071 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002072
2073 JSAMPROW
2074 scanline[1];
2075
cristy3ed852e2009-09-05 21:47:34 +00002076 MagickBooleanType
2077 status;
2078
cristy22646e22013-06-23 16:34:03 +00002079 MemoryInfo
2080 *memory_info;
2081
cristy3ed852e2009-09-05 21:47:34 +00002082 register JSAMPLE
2083 *q;
2084
cristybb503372010-05-27 20:51:26 +00002085 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002086 i;
2087
cristy524222d2011-04-25 00:37:06 +00002088 ssize_t
2089 y;
2090
cristy3ed852e2009-09-05 21:47:34 +00002091 struct jpeg_compress_struct
2092 jpeg_info;
2093
2094 struct jpeg_error_mgr
2095 jpeg_error;
2096
cristy5328a4c2013-12-03 11:32:13 +00002097 unsigned short
2098 scale;
2099
cristy3ed852e2009-09-05 21:47:34 +00002100 /*
2101 Open image file.
2102 */
2103 assert(image_info != (const ImageInfo *) NULL);
2104 assert(image_info->signature == MagickSignature);
2105 assert(image != (Image *) NULL);
2106 assert(image->signature == MagickSignature);
2107 if (image->debug != MagickFalse)
2108 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002109 assert(exception != (ExceptionInfo *) NULL);
2110 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00002111 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002112 if (status == MagickFalse)
2113 return(status);
2114 /*
2115 Initialize JPEG parameters.
2116 */
cristy91044972011-04-22 14:21:16 +00002117 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002118 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2119 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2120 jpeg_info.client_data=(void *) image;
2121 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002122 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002123 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002124 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002125 error_manager.image=image;
cristy72f87f82013-06-23 16:43:05 +00002126 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002127 if (setjmp(error_manager.error_recovery) != 0)
2128 {
2129 jpeg_destroy_compress(&jpeg_info);
2130 (void) CloseBlob(image);
2131 return(MagickFalse);
2132 }
2133 jpeg_info.client_data=(void *) &error_manager;
2134 jpeg_create_compress(&jpeg_info);
2135 JPEGDestinationManager(&jpeg_info,image);
2136 if ((image->columns != (unsigned int) image->columns) ||
2137 (image->rows != (unsigned int) image->rows))
2138 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2139 jpeg_info.image_width=(unsigned int) image->columns;
2140 jpeg_info.image_height=(unsigned int) image->rows;
2141 jpeg_info.input_components=3;
2142 jpeg_info.data_precision=8;
2143 jpeg_info.in_color_space=JCS_RGB;
2144 switch (image->colorspace)
2145 {
2146 case CMYKColorspace:
2147 {
2148 jpeg_info.input_components=4;
2149 jpeg_info.in_color_space=JCS_CMYK;
2150 break;
2151 }
2152 case YCbCrColorspace:
2153 case Rec601YCbCrColorspace:
2154 case Rec709YCbCrColorspace:
2155 {
2156 jpeg_info.in_color_space=JCS_YCbCr;
2157 break;
2158 }
2159 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002160 {
cristye9355552014-01-16 13:35:05 +00002161 if (image_info->type == TrueColorType)
2162 break;
cristy3ed852e2009-09-05 21:47:34 +00002163 jpeg_info.input_components=1;
2164 jpeg_info.in_color_space=JCS_GRAYSCALE;
2165 break;
2166 }
2167 default:
cristy9f396782009-12-21 01:37:45 +00002168 {
cristyaf8d3912014-02-21 14:50:33 +00002169 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye9355552014-01-16 13:35:05 +00002170 if (image_info->type == TrueColorType)
2171 break;
cristyc47ba2d2014-01-16 19:41:18 +00002172 if (IsImageGray(image,exception) != MagickFalse)
cristye9355552014-01-16 13:35:05 +00002173 {
2174 jpeg_info.input_components=1;
2175 jpeg_info.in_color_space=JCS_GRAYSCALE;
2176 }
cristy3ed852e2009-09-05 21:47:34 +00002177 break;
cristy9f396782009-12-21 01:37:45 +00002178 }
cristy3ed852e2009-09-05 21:47:34 +00002179 }
cristy3ed852e2009-09-05 21:47:34 +00002180 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002181 if (jpeg_info.in_color_space == JCS_CMYK)
2182 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002183 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2184 jpeg_info.data_precision=8;
2185 else
dirk93b02b72013-11-16 16:03:36 +00002186 jpeg_info.data_precision=BITS_IN_JSAMPLE;
cristy3ed852e2009-09-05 21:47:34 +00002187 jpeg_info.density_unit=(UINT8) 1;
2188 if (image->debug != MagickFalse)
2189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002190 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2191 floor(image->resolution.y+0.5));
2192 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002193 {
2194 /*
2195 Set image resolution.
2196 */
cristydf878012014-01-12 23:39:40 +00002197 jpeg_info.write_JFIF_header=TRUE;
cristy2a11bef2011-10-28 18:33:11 +00002198 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2199 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy0c220632013-09-25 22:10:28 +00002200 /*
2201 Set image resolution units.
2202 */
2203 jpeg_info.density_unit=(UINT8) 0;
cristy3ed852e2009-09-05 21:47:34 +00002204 if (image->units == PixelsPerInchResolution)
2205 jpeg_info.density_unit=(UINT8) 1;
2206 if (image->units == PixelsPerCentimeterResolution)
2207 jpeg_info.density_unit=(UINT8) 2;
2208 }
cristy97cb3bf2012-02-21 18:31:20 +00002209 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002210 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002211 if (option != (const char *) NULL)
2212 switch (*option)
2213 {
2214 case 'D':
2215 case 'd':
2216 {
2217 if (LocaleCompare(option,"default") == 0)
2218 jpeg_info.dct_method=JDCT_DEFAULT;
2219 break;
2220 }
2221 case 'F':
2222 case 'f':
2223 {
2224 if (LocaleCompare(option,"fastest") == 0)
2225 jpeg_info.dct_method=JDCT_FASTEST;
2226 if (LocaleCompare(option,"float") == 0)
2227 jpeg_info.dct_method=JDCT_FLOAT;
2228 break;
2229 }
2230 case 'I':
2231 case 'i':
2232 {
2233 if (LocaleCompare(option,"ifast") == 0)
2234 jpeg_info.dct_method=JDCT_IFAST;
2235 if (LocaleCompare(option,"islow") == 0)
2236 jpeg_info.dct_method=JDCT_ISLOW;
2237 break;
2238 }
2239 }
cristy092ec8d2013-04-26 13:46:22 +00002240 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002241 if (option != (const char *) NULL)
cristydf878012014-01-12 23:39:40 +00002242 jpeg_info.optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE : FALSE;
cristy3ed852e2009-09-05 21:47:34 +00002243 else
2244 {
2245 MagickSizeType
2246 length;
2247
2248 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2249 image->rows*sizeof(JSAMPLE);
2250 if (length == (MagickSizeType) ((size_t) length))
2251 {
2252 /*
2253 Perform optimization only if available memory resources permit it.
2254 */
2255 status=AcquireMagickResource(MemoryResource,length);
cristy3ed852e2009-09-05 21:47:34 +00002256 RelinquishMagickResource(MemoryResource,length);
anthony6f201312012-03-30 04:08:15 +00002257 jpeg_info.optimize_coding=status;
cristy3ed852e2009-09-05 21:47:34 +00002258 }
2259 }
2260#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2261 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2262 (image_info->interlace != NoInterlace))
2263 {
2264 if (image->debug != MagickFalse)
2265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2266 "Interlace: progressive");
2267 jpeg_simple_progression(&jpeg_info);
2268 }
2269 else
2270 if (image->debug != MagickFalse)
2271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2272 "Interlace: non-progressive");
2273#else
2274 if (image->debug != MagickFalse)
2275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2276 "Interlace: nonprogressive");
2277#endif
dirk871abab2013-11-03 13:21:16 +00002278 quality=92;
cristy2a950402014-03-12 17:57:14 +00002279 if ((image_info->compression != LosslessJPEGCompression) &&
2280 (image->quality <= 100))
2281 {
2282 if (image->quality != UndefinedCompressionQuality)
2283 quality=(int) image->quality;
2284 if (image->debug != MagickFalse)
2285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2286 (double) image->quality);
2287 }
2288 else
2289 {
2290#if !defined(C_LOSSLESS_SUPPORTED)
2291 quality=100;
2292 if (image->debug != MagickFalse)
2293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2294#else
2295 if (image->quality < 100)
2296 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2297 "LosslessToLossyJPEGConversion",image->filename);
2298 else
2299 {
2300 int
2301 point_transform,
2302 predictor;
2303
2304 predictor=image->quality/100; /* range 1-7 */
2305 point_transform=image->quality % 20; /* range 0-15 */
2306 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2307 if (image->debug != MagickFalse)
2308 {
2309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2310 "Compression: lossless");
2311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2312 "Predictor: %d",predictor);
2313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2314 "Point Transform: %d",point_transform);
2315 }
2316 }
2317#endif
2318 }
cristy092ec8d2013-04-26 13:46:22 +00002319 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002320 if (option != (const char *) NULL)
2321 {
2322 Image
2323 *jpeg_image;
2324
2325 ImageInfo
2326 *jpeg_info;
2327
2328 jpeg_info=CloneImageInfo(image_info);
cristye0823d52013-11-03 21:50:10 +00002329 jpeg_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002330 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002331 if (jpeg_image != (Image *) NULL)
2332 {
2333 MagickSizeType
2334 extent;
2335
2336 size_t
cristy87e73ab2010-02-12 01:59:12 +00002337 maximum,
2338 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002339
2340 /*
2341 Search for compression quality that does not exceed image extent.
2342 */
2343 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002344 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002345 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002346 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
cristye0a316c2010-02-13 19:15:23 +00002347 maximum=101;
dirkcf845242013-08-11 21:36:19 +00002348 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002349 {
cristy3975f402014-02-26 14:36:33 +00002350 (void) AcquireUniqueFilename(jpeg_image->filename);
2351 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
cristy1e178e72011-08-28 19:44:34 +00002352 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002353 if (GetBlobSize(jpeg_image) <= extent)
cristy3975f402014-02-26 14:36:33 +00002354 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002355 else
cristy3975f402014-02-26 14:36:33 +00002356 maximum=jpeg_image->quality-1;
2357 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristy0adb4f92009-11-28 18:08:51 +00002358 }
dirk871abab2013-11-03 13:21:16 +00002359 quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002360 jpeg_image=DestroyImage(jpeg_image);
2361 }
2362 jpeg_info=DestroyImageInfo(jpeg_info);
2363 }
cristy21749e92012-02-18 02:29:21 +00002364 jpeg_set_quality(&jpeg_info,quality,MagickTrue);
cristy16e74722012-07-01 13:01:38 +00002365#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002366 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002367 if (option != (const char *) NULL)
2368 {
2369 GeometryInfo
2370 geometry_info;
2371
2372 int
2373 flags;
2374
2375 /*
2376 Set quality scaling for luminance and chrominance separately.
2377 */
2378 flags=ParseGeometry(option,&geometry_info);
2379 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2380 {
2381 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2382 (geometry_info.rho+0.5));
2383 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2384 (geometry_info.sigma+0.5));
2385 jpeg_default_qtables(&jpeg_info,MagickTrue);
2386 }
2387 }
2388#endif
cristy7b8ed292013-05-18 01:13:56 +00002389 colorspace=jpeg_info.in_color_space;
2390 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002391 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002392 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002393 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002394 colorspace=StringToInteger(value);
2395 sampling_factor=(const char *) NULL;
2396 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002397 {
cristy7b8ed292013-05-18 01:13:56 +00002398 value=GetImageOption(image_info,"jpeg:sampling-factor");
2399 if (value == (char *) NULL)
2400 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2401 if (value != (char *) NULL)
2402 {
2403 sampling_factor=value;
2404 if (image->debug != MagickFalse)
2405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406 " Input sampling-factors=%s",sampling_factor);
2407 }
cristy3ed852e2009-09-05 21:47:34 +00002408 }
cristy7b8ed292013-05-18 01:13:56 +00002409 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002410 if (image_info->sampling_factor != (char *) NULL)
2411 sampling_factor=image_info->sampling_factor;
2412 if (sampling_factor == (const char *) NULL)
2413 {
2414 if (image->quality >= 90)
2415 for (i=0; i < MAX_COMPONENTS; i++)
2416 {
2417 jpeg_info.comp_info[i].h_samp_factor=1;
2418 jpeg_info.comp_info[i].v_samp_factor=1;
2419 }
2420 }
2421 else
2422 {
2423 char
2424 **factors;
2425
2426 GeometryInfo
2427 geometry_info;
2428
2429 MagickStatusType
2430 flags;
2431
2432 /*
2433 Set sampling factor.
2434 */
2435 i=0;
2436 factors=SamplingFactorToList(sampling_factor);
2437 if (factors != (char **) NULL)
2438 {
2439 for (i=0; i < MAX_COMPONENTS; i++)
2440 {
2441 if (factors[i] == (char *) NULL)
2442 break;
2443 flags=ParseGeometry(factors[i],&geometry_info);
2444 if ((flags & SigmaValue) == 0)
2445 geometry_info.sigma=geometry_info.rho;
2446 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2447 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2448 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2449 }
2450 factors=(char **) RelinquishMagickMemory(factors);
2451 }
2452 for ( ; i < MAX_COMPONENTS; i++)
2453 {
2454 jpeg_info.comp_info[i].h_samp_factor=1;
2455 jpeg_info.comp_info[i].v_samp_factor=1;
2456 }
2457 }
cristy092ec8d2013-04-26 13:46:22 +00002458 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002459 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002460 {
cristy1b58f252012-03-01 01:41:41 +00002461 QuantizationTable
2462 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002463
cristy1445b322012-02-19 18:57:30 +00002464 /*
cristy1b58f252012-03-01 01:41:41 +00002465 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002466 */
cristy1b58f252012-03-01 01:41:41 +00002467 table=GetQuantizationTable(option,"0",exception);
2468 if (table != (QuantizationTable *) NULL)
2469 {
cristyae453b72013-04-21 22:24:20 +00002470 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002471 jpeg_info.comp_info[i].quant_tbl_no=0;
2472 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2473 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002474 table=DestroyQuantizationTable(table);
2475 }
2476 table=GetQuantizationTable(option,"1",exception);
2477 if (table != (QuantizationTable *) NULL)
2478 {
cristyae453b72013-04-21 22:24:20 +00002479 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002480 jpeg_info.comp_info[i].quant_tbl_no=1;
2481 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2482 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002483 table=DestroyQuantizationTable(table);
2484 }
2485 table=GetQuantizationTable(option,"2",exception);
2486 if (table != (QuantizationTable *) NULL)
2487 {
cristyae453b72013-04-21 22:24:20 +00002488 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002489 jpeg_info.comp_info[i].quant_tbl_no=2;
2490 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2491 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002492 table=DestroyQuantizationTable(table);
2493 }
2494 table=GetQuantizationTable(option,"3",exception);
2495 if (table != (QuantizationTable *) NULL)
2496 {
cristyae453b72013-04-21 22:24:20 +00002497 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002498 jpeg_info.comp_info[i].quant_tbl_no=3;
2499 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2500 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002501 table=DestroyQuantizationTable(table);
2502 }
cristy1445b322012-02-19 18:57:30 +00002503 }
cristy3ed852e2009-09-05 21:47:34 +00002504 jpeg_start_compress(&jpeg_info,MagickTrue);
2505 if (image->debug != MagickFalse)
2506 {
2507 if (image->storage_class == PseudoClass)
2508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2509 "Storage class: PseudoClass");
2510 else
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2514 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002515 if (image->colors != 0)
2516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002517 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002518 else
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 "Number of colors: unspecified");
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2523 switch (image->colorspace)
2524 {
2525 case CMYKColorspace:
2526 {
2527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2528 "Storage class: DirectClass");
2529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2530 "Colorspace: CMYK");
2531 break;
2532 }
2533 case YCbCrColorspace:
2534 case Rec601YCbCrColorspace:
2535 case Rec709YCbCrColorspace:
2536 {
2537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538 "Colorspace: YCbCr");
2539 break;
2540 }
2541 default:
2542 break;
2543 }
2544 switch (image->colorspace)
2545 {
2546 case CMYKColorspace:
2547 {
2548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2549 "Colorspace: CMYK");
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2552 jpeg_info.comp_info[0].h_samp_factor,
2553 jpeg_info.comp_info[0].v_samp_factor,
2554 jpeg_info.comp_info[1].h_samp_factor,
2555 jpeg_info.comp_info[1].v_samp_factor,
2556 jpeg_info.comp_info[2].h_samp_factor,
2557 jpeg_info.comp_info[2].v_samp_factor,
2558 jpeg_info.comp_info[3].h_samp_factor,
2559 jpeg_info.comp_info[3].v_samp_factor);
2560 break;
2561 }
2562 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002563 {
2564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2565 "Colorspace: GRAY");
2566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2567 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2568 jpeg_info.comp_info[0].v_samp_factor);
2569 break;
2570 }
cristyed8d7852013-08-01 22:44:22 +00002571 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002572 case RGBColorspace:
2573 {
2574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002575 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2577 "Sampling factors: %dx%d,%dx%d,%dx%d",
2578 jpeg_info.comp_info[0].h_samp_factor,
2579 jpeg_info.comp_info[0].v_samp_factor,
2580 jpeg_info.comp_info[1].h_samp_factor,
2581 jpeg_info.comp_info[1].v_samp_factor,
2582 jpeg_info.comp_info[2].h_samp_factor,
2583 jpeg_info.comp_info[2].v_samp_factor);
2584 break;
2585 }
2586 case YCbCrColorspace:
2587 case Rec601YCbCrColorspace:
2588 case Rec709YCbCrColorspace:
2589 {
2590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2591 "Colorspace: YCbCr");
2592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593 "Sampling factors: %dx%d,%dx%d,%dx%d",
2594 jpeg_info.comp_info[0].h_samp_factor,
2595 jpeg_info.comp_info[0].v_samp_factor,
2596 jpeg_info.comp_info[1].h_samp_factor,
2597 jpeg_info.comp_info[1].v_samp_factor,
2598 jpeg_info.comp_info[2].h_samp_factor,
2599 jpeg_info.comp_info[2].v_samp_factor);
2600 break;
2601 }
2602 default:
2603 {
2604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2605 image->colorspace);
2606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2607 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2608 jpeg_info.comp_info[0].h_samp_factor,
2609 jpeg_info.comp_info[0].v_samp_factor,
2610 jpeg_info.comp_info[1].h_samp_factor,
2611 jpeg_info.comp_info[1].v_samp_factor,
2612 jpeg_info.comp_info[2].h_samp_factor,
2613 jpeg_info.comp_info[2].v_samp_factor,
2614 jpeg_info.comp_info[3].h_samp_factor,
2615 jpeg_info.comp_info[3].v_samp_factor);
2616 break;
2617 }
2618 }
2619 }
2620 /*
2621 Write JPEG profiles.
2622 */
cristyd15e6592011-10-15 00:13:06 +00002623 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002624 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002625 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002626 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2627 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2628 if (image->profiles != (void *) NULL)
2629 WriteProfile(&jpeg_info,image);
2630 /*
2631 Convert MIFF to JPEG raster pixels.
2632 */
cristy22646e22013-06-23 16:34:03 +00002633 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002634 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002635 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002636 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002637 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002638 if (setjmp(error_manager.error_recovery) != 0)
2639 {
2640 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002641 if (memory_info != (MemoryInfo *) NULL)
2642 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002643 (void) CloseBlob(image);
2644 return(MagickFalse);
2645 }
2646 scanline[0]=(JSAMPROW) jpeg_pixels;
cristy477b9fd2013-12-03 16:52:31 +00002647 scale=65535U/GetQuantumRange(jpeg_info.data_precision);
cristy31db6c12013-12-04 15:17:30 +00002648 if (scale == 0)
2649 scale=1;
cristye90d7402010-03-14 18:21:29 +00002650 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
cristy3ed852e2009-09-05 21:47:34 +00002652 if ((jpeg_info.in_color_space == JCS_RGB) ||
2653 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002654 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002655 {
cristy4c08aed2011-07-01 19:47:50 +00002656 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002657 *p;
2658
cristybb503372010-05-27 20:51:26 +00002659 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002660 x;
2661
cristy1e178e72011-08-28 19:44:34 +00002662 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002663 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002664 break;
2665 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002666 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002667 {
cristy4c08aed2011-07-01 19:47:50 +00002668 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2669 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2670 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002671 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002672 }
2673 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002674 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2675 image->rows);
2676 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002677 break;
2678 }
2679 else
cristye90d7402010-03-14 18:21:29 +00002680 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002681 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002682 {
cristyd0323222013-04-07 16:13:21 +00002683 register const Quantum
2684 *p;
2685
2686 register ssize_t
2687 x;
2688
2689 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2690 if (p == (const Quantum *) NULL)
2691 break;
2692 q=jpeg_pixels;
2693 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002694 {
cristyd0323222013-04-07 16:13:21 +00002695 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2696 image,p)));
2697 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002698 }
cristyd0323222013-04-07 16:13:21 +00002699 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2700 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2701 image->rows);
2702 if (status == MagickFalse)
2703 break;
2704 }
cristye90d7402010-03-14 18:21:29 +00002705 else
cristybb503372010-05-27 20:51:26 +00002706 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002707 {
cristy4c08aed2011-07-01 19:47:50 +00002708 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002709 *p;
2710
cristybb503372010-05-27 20:51:26 +00002711 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002712 x;
2713
cristy1e178e72011-08-28 19:44:34 +00002714 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002715 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002716 break;
2717 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002718 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002719 {
2720 /*
2721 Convert DirectClass packets to contiguous CMYK scanlines.
2722 */
2723 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002724 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002725 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002726 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002727 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002728 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002729 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002730 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002731 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002732 }
2733 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002734 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2735 image->rows);
2736 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002737 break;
2738 }
2739 }
2740 else
2741 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002742 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002743 {
cristy4c08aed2011-07-01 19:47:50 +00002744 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002745 *p;
2746
cristybb503372010-05-27 20:51:26 +00002747 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002748 x;
2749
cristy1e178e72011-08-28 19:44:34 +00002750 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002751 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002752 break;
2753 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002754 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002755 {
cristy217ddd62013-12-06 23:27:09 +00002756 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2757 p)))/scale);
cristyed231572011-07-14 02:18:59 +00002758 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002759 }
2760 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002761 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2762 image->rows);
2763 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002764 break;
2765 }
2766 else
2767 if ((jpeg_info.in_color_space == JCS_RGB) ||
2768 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002769 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002770 {
cristy4c08aed2011-07-01 19:47:50 +00002771 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002772 *p;
2773
cristybb503372010-05-27 20:51:26 +00002774 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002775 x;
2776
cristy1e178e72011-08-28 19:44:34 +00002777 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002778 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002779 break;
2780 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002781 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002782 {
cristy5328a4c2013-12-03 11:32:13 +00002783 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2784 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2785 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002786 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002787 }
2788 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002789 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002790 image->rows);
cristye90d7402010-03-14 18:21:29 +00002791 if (status == MagickFalse)
2792 break;
2793 }
2794 else
cristybb503372010-05-27 20:51:26 +00002795 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002796 {
cristy4c08aed2011-07-01 19:47:50 +00002797 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002798 *p;
2799
cristybb503372010-05-27 20:51:26 +00002800 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002801 x;
2802
cristy1e178e72011-08-28 19:44:34 +00002803 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002804 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002805 break;
2806 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002807 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002808 {
2809 /*
2810 Convert DirectClass packets to contiguous CMYK scanlines.
2811 */
cristy217ddd62013-12-06 23:27:09 +00002812 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2813 image,p))/scale);
2814 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2815 image,p))/scale);
2816 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2817 image,p))/scale);
2818 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2819 image,p))/scale);
cristyed231572011-07-14 02:18:59 +00002820 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002821 }
2822 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002823 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002824 image->rows);
cristye90d7402010-03-14 18:21:29 +00002825 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002826 break;
2827 }
cristybb503372010-05-27 20:51:26 +00002828 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002829 jpeg_finish_compress(&jpeg_info);
2830 /*
2831 Relinquish resources.
2832 */
2833 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002834 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002835 (void) CloseBlob(image);
2836 return(MagickTrue);
2837}
2838#endif