blob: 5dbe2fe508787a8ab04d5636454f3b15995277d9 [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 %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy45ef08f2012-12-07 13:13:34 +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"
78#include "MagickCore/splay-tree.h"
79#include "MagickCore/static.h"
80#include "MagickCore/string_.h"
81#include "MagickCore/string-private.h"
cristye40005d2012-03-23 12:18:45 +000082#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000083#include "MagickCore/utility.h"
cristy1b58f252012-03-01 01:41:41 +000084#include "MagickCore/xml-tree.h"
85#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000086#include <setjmp.h>
87#if defined(MAGICKCORE_JPEG_DELEGATE)
88#define JPEG_INTERNAL_OPTIONS
cristy07a3cca2012-12-10 13:09:10 +000089#if defined(__MINGW32__) || defined(__MINGW64__)
cristy3ed852e2009-09-05 21:47:34 +000090# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
91#endif
92#undef HAVE_STDLIB_H
93#include "jpeglib.h"
94#include "jerror.h"
95#endif
dirk29dd80e2013-10-31 23:11:11 +000096
97
cristy3ed852e2009-09-05 21:47:34 +000098/*
99 Define declarations.
100*/
101#define ICC_MARKER (JPEG_APP0+2)
102#define ICC_PROFILE "ICC_PROFILE"
103#define IPTC_MARKER (JPEG_APP0+13)
104#define XML_MARKER (JPEG_APP0+1)
cristyeb9759e2012-06-07 23:06:29 +0000105#define MaxBufferExtent 16384
dirk29dd80e2013-10-31 23:11:11 +0000106
107
cristy3ed852e2009-09-05 21:47:34 +0000108/*
109 Typedef declarations.
110*/
111#if defined(MAGICKCORE_JPEG_DELEGATE)
112typedef struct _DestinationManager
113{
114 struct jpeg_destination_mgr
115 manager;
116
117 Image
118 *image;
119
120 JOCTET
121 *buffer;
122} DestinationManager;
123
124typedef struct _ErrorManager
125{
cristy018f07f2011-09-04 21:15:19 +0000126 ExceptionInfo
127 *exception;
128
cristy3ed852e2009-09-05 21:47:34 +0000129 Image
130 *image;
131
cristyd28b1dd2011-05-14 20:30:38 +0000132 MagickBooleanType
133 finished;
134
cristyf551aee2012-09-27 21:45:23 +0000135 StringInfo
136 *profile;
137
cristy3ed852e2009-09-05 21:47:34 +0000138 jmp_buf
139 error_recovery;
140} ErrorManager;
141
142typedef struct _SourceManager
143{
144 struct jpeg_source_mgr
145 manager;
146
147 Image
148 *image;
149
150 JOCTET
151 *buffer;
152
153 boolean
154 start_of_blob;
155} SourceManager;
156#endif
cristy1b58f252012-03-01 01:41:41 +0000157
158typedef struct _QuantizationTable
159{
160 char
161 *slot,
162 *description;
163
164 size_t
165 width,
166 height;
167
cristy043f3f32012-03-02 17:37:28 +0000168 double
169 divisor;
170
cristy1b58f252012-03-01 01:41:41 +0000171 unsigned int
cristy1b58f252012-03-01 01:41:41 +0000172 *levels;
173} QuantizationTable;
dirk29dd80e2013-10-31 23:11:11 +0000174
175
cristy3ed852e2009-09-05 21:47:34 +0000176/*
177 Forward declarations.
178*/
179#if defined(MAGICKCORE_JPEG_DELEGATE)
180static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000181 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000182#endif
dirk29dd80e2013-10-31 23:11:11 +0000183
184
cristy3ed852e2009-09-05 21:47:34 +0000185/*
186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187% %
188% %
189% %
190% I s J P E G %
191% %
192% %
193% %
194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195%
196% IsJPEG() returns MagickTrue if the image format type, identified by the
197% magick string, is JPEG.
198%
199% The format of the IsJPEG method is:
200%
201% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
202%
203% A description of each parameter follows:
204%
205% o magick: compare image format pattern against these bytes.
206%
207% o length: Specifies the length of the magick string.
208%
209*/
210static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
211{
212 if (length < 3)
213 return(MagickFalse);
214 if (memcmp(magick,"\377\330\377",3) == 0)
215 return(MagickTrue);
216 return(MagickFalse);
217}
dirk29dd80e2013-10-31 23:11:11 +0000218
219
cristy3ed852e2009-09-05 21:47:34 +0000220#if defined(MAGICKCORE_JPEG_DELEGATE)
221/*
222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223% %
224% %
225% %
226% R e a d J P E G I m a g e %
227% %
228% %
229% %
230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231%
232% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
233% the memory necessary for the new Image structure and returns a pointer to
234% the new image.
235%
236% The format of the ReadJPEGImage method is:
237%
238% Image *ReadJPEGImage(const ImageInfo *image_info,
239% ExceptionInfo *exception)
240%
241% A description of each parameter follows:
242%
243% o image_info: the image info.
244%
245% o exception: return any errors or warnings in this structure.
246%
247*/
248
cristy3ed852e2009-09-05 21:47:34 +0000249static boolean FillInputBuffer(j_decompress_ptr cinfo)
250{
251 SourceManager
252 *source;
253
254 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000255 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
256 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000257 if (source->manager.bytes_in_buffer == 0)
258 {
cristy453b8202012-06-07 22:57:52 +0000259 if (source->start_of_blob != FALSE)
cristy3ed852e2009-09-05 21:47:34 +0000260 ERREXIT(cinfo,JERR_INPUT_EMPTY);
261 WARNMS(cinfo,JWRN_JPEG_EOF);
262 source->buffer[0]=(JOCTET) 0xff;
263 source->buffer[1]=(JOCTET) JPEG_EOI;
264 source->manager.bytes_in_buffer=2;
265 }
266 source->manager.next_input_byte=source->buffer;
267 source->start_of_blob=FALSE;
268 return(TRUE);
269}
270
271static int GetCharacter(j_decompress_ptr jpeg_info)
272{
273 if (jpeg_info->src->bytes_in_buffer == 0)
274 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
275 jpeg_info->src->bytes_in_buffer--;
276 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
277}
278
279static void InitializeSource(j_decompress_ptr cinfo)
280{
281 SourceManager
282 *source;
283
284 source=(SourceManager *) cinfo->src;
285 source->start_of_blob=TRUE;
286}
287
cristye8dd1302009-11-11 02:45:03 +0000288static MagickBooleanType IsITUFaxImage(const Image *image)
289{
290 const StringInfo
291 *profile;
292
293 const unsigned char
294 *datum;
295
cristyace6aa42009-11-11 03:17:33 +0000296 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000297 if (profile == (const StringInfo *) NULL)
298 return(MagickFalse);
299 if (GetStringInfoLength(profile) < 5)
300 return(MagickFalse);
301 datum=GetStringInfoDatum(profile);
302 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
303 (datum[3] == 0x41) && (datum[4] == 0x58))
304 return(MagickTrue);
305 return(MagickFalse);
306}
307
cristy437c3932011-10-12 18:03:46 +0000308static void JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000309{
cristyd28b1dd2011-05-14 20:30:38 +0000310 char
311 message[JMSG_LENGTH_MAX];
312
cristy3ed852e2009-09-05 21:47:34 +0000313 ErrorManager
314 *error_manager;
315
cristyc82a27b2011-10-21 01:07:16 +0000316 ExceptionInfo
317 *exception;
318
cristyd28b1dd2011-05-14 20:30:38 +0000319 Image
320 *image;
321
322 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000323 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000324 image=error_manager->image;
cristyc82a27b2011-10-21 01:07:16 +0000325 exception=error_manager->exception;
cristy86f33542011-05-21 22:58:33 +0000326 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000327 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
329 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000330 if (error_manager->finished != MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000331 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
332 (char *) message,"`%s'",image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000333 else
cristyc82a27b2011-10-21 01:07:16 +0000334 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
335 (char *) message,"`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000336 longjmp(error_manager->error_recovery,1);
337}
338
cristyd28b1dd2011-05-14 20:30:38 +0000339static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
340{
cristyf162e012012-03-28 12:54:33 +0000341#define JPEGExcessiveWarnings 1000
342
cristyd28b1dd2011-05-14 20:30:38 +0000343 char
344 message[JMSG_LENGTH_MAX];
345
346 ErrorManager
347 *error_manager;
348
cristy018f07f2011-09-04 21:15:19 +0000349 ExceptionInfo
350 *exception;
351
cristyd28b1dd2011-05-14 20:30:38 +0000352 Image
353 *image;
354
355 *message='\0';
356 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000357 exception=error_manager->exception;
cristyd28b1dd2011-05-14 20:30:38 +0000358 image=error_manager->image;
359 if (level < 0)
360 {
361 /*
362 Process warning message.
363 */
364 (jpeg_info->err->format_message)(jpeg_info,message);
cristyf162e012012-03-28 12:54:33 +0000365 if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
366 JPEGErrorHandler(jpeg_info);
cristy8043fb32012-06-28 16:14:12 +0000367 ThrowBinaryException(CorruptImageWarning,(char *) message,
368 image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000369 }
370 else
371 if ((image->debug != MagickFalse) &&
372 (level >= jpeg_info->err->trace_level))
373 {
374 /*
375 Process trace message.
376 */
377 (jpeg_info->err->format_message)(jpeg_info,message);
378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
379 "[%s] JPEG Trace: \"%s\"",image->filename,message);
380 }
381 return(MagickTrue);
382}
383
cristy3ed852e2009-09-05 21:47:34 +0000384static boolean ReadComment(j_decompress_ptr jpeg_info)
385{
cristy3ed852e2009-09-05 21:47:34 +0000386 ErrorManager
387 *error_manager;
388
cristy018f07f2011-09-04 21:15:19 +0000389 ExceptionInfo
390 *exception;
391
cristy3ed852e2009-09-05 21:47:34 +0000392 Image
393 *image;
394
cristyf551aee2012-09-27 21:45:23 +0000395 register unsigned char
cristy3ed852e2009-09-05 21:47:34 +0000396 *p;
397
cristybb503372010-05-27 20:51:26 +0000398 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000399 i;
400
401 size_t
402 length;
403
cristyf551aee2012-09-27 21:45:23 +0000404 StringInfo
405 *comment;
406
cristy3ed852e2009-09-05 21:47:34 +0000407 /*
408 Determine length of comment.
409 */
410 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000411 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000412 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000413 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000414 length+=GetCharacter(jpeg_info);
415 length-=2;
416 if (length <= 0)
417 return(MagickTrue);
cristyf551aee2012-09-27 21:45:23 +0000418 comment=BlobToStringInfo((const void *) NULL,length);
419 if (comment == (StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000420 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
421 image->filename);
422 /*
423 Read comment.
424 */
cristyf551aee2012-09-27 21:45:23 +0000425 error_manager->profile=comment;
426 p=GetStringInfoDatum(comment);
427 for (i=0; i < (ssize_t) GetStringInfoLength(comment); i++)
428 *p++=(unsigned char) GetCharacter(jpeg_info);
cristy3ed852e2009-09-05 21:47:34 +0000429 *p='\0';
cristyf551aee2012-09-27 21:45:23 +0000430 error_manager->profile=NULL;
431 p=GetStringInfoDatum(comment);
432 (void) SetImageProperty(image,"comment",(const char *) p,exception);
433 comment=DestroyStringInfo(comment);
cristy3ed852e2009-09-05 21:47:34 +0000434 return(MagickTrue);
435}
436
437static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
438{
439 char
440 magick[12];
441
442 ErrorManager
443 *error_manager;
444
cristy018f07f2011-09-04 21:15:19 +0000445 ExceptionInfo
446 *exception;
447
cristy3ed852e2009-09-05 21:47:34 +0000448 Image
449 *image;
450
451 MagickBooleanType
452 status;
453
cristybb503372010-05-27 20:51:26 +0000454 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000455 i;
456
457 register unsigned char
458 *p;
459
460 size_t
461 length;
462
463 StringInfo
464 *icc_profile,
465 *profile;
466
467 /*
468 Read color profile.
469 */
cristybb503372010-05-27 20:51:26 +0000470 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000471 length+=(size_t) GetCharacter(jpeg_info);
472 length-=2;
473 if (length <= 14)
474 {
475 while (length-- > 0)
476 (void) GetCharacter(jpeg_info);
477 return(MagickTrue);
478 }
479 for (i=0; i < 12; i++)
480 magick[i]=(char) GetCharacter(jpeg_info);
481 if (LocaleCompare(magick,ICC_PROFILE) != 0)
482 {
483 /*
484 Not a ICC profile, return.
485 */
cristybb503372010-05-27 20:51:26 +0000486 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000487 (void) GetCharacter(jpeg_info);
488 return(MagickTrue);
489 }
490 (void) GetCharacter(jpeg_info); /* id */
491 (void) GetCharacter(jpeg_info); /* markers */
492 length-=14;
493 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000494 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000495 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000496 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000497 if (profile == (StringInfo *) NULL)
498 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
499 image->filename);
cristyf551aee2012-09-27 21:45:23 +0000500 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000501 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000502 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000503 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000504 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000505 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
506 if (icc_profile != (StringInfo *) NULL)
507 {
508 ConcatenateStringInfo(icc_profile,profile);
509 profile=DestroyStringInfo(profile);
510 }
511 else
512 {
cristyd15e6592011-10-15 00:13:06 +0000513 status=SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000514 profile=DestroyStringInfo(profile);
515 if (status == MagickFalse)
516 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
517 image->filename);
518 }
519 if (image->debug != MagickFalse)
520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000521 "Profile: ICC, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000522 return(MagickTrue);
523}
524
525static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
526{
527 char
528 magick[MaxTextExtent];
529
530 ErrorManager
531 *error_manager;
532
cristy018f07f2011-09-04 21:15:19 +0000533 ExceptionInfo
534 *exception;
535
cristy3ed852e2009-09-05 21:47:34 +0000536 Image
537 *image;
538
539 MagickBooleanType
540 status;
541
cristybb503372010-05-27 20:51:26 +0000542 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000543 i;
544
545 register unsigned char
546 *p;
547
548 size_t
549 length;
550
551 StringInfo
552 *iptc_profile,
553 *profile;
554
555 /*
556 Determine length of binary data stored here.
557 */
cristybb503372010-05-27 20:51:26 +0000558 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000559 length+=(size_t) GetCharacter(jpeg_info);
560 length-=2;
561 if (length <= 14)
562 {
563 while (length-- > 0)
564 (void) GetCharacter(jpeg_info);
565 return(MagickTrue);
566 }
567 /*
568 Validate that this was written as a Photoshop resource format slug.
569 */
570 for (i=0; i < 10; i++)
571 magick[i]=(char) GetCharacter(jpeg_info);
572 magick[10]='\0';
573 if (length <= 10)
574 return(MagickTrue);
575 length-=10;
576 if (LocaleCompare(magick,"Photoshop ") != 0)
577 {
578 /*
579 Not a IPTC profile, return.
580 */
cristybb503372010-05-27 20:51:26 +0000581 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000582 (void) GetCharacter(jpeg_info);
583 return(MagickTrue);
584 }
585 /*
586 Remove the version number.
587 */
588 for (i=0; i < 4; i++)
589 (void) GetCharacter(jpeg_info);
590 if (length <= 4)
591 return(MagickTrue);
592 length-=4;
593 if (length == 0)
594 return(MagickTrue);
595 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000596 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000597 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000598 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000599 if (profile == (StringInfo *) NULL)
600 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
601 image->filename);
cristyf551aee2012-09-27 21:45:23 +0000602 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000603 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000604 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000605 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000606 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000607 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
608 if (iptc_profile != (StringInfo *) NULL)
609 {
610 ConcatenateStringInfo(iptc_profile,profile);
611 profile=DestroyStringInfo(profile);
612 }
613 else
614 {
cristyd15e6592011-10-15 00:13:06 +0000615 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000616 profile=DestroyStringInfo(profile);
617 if (status == MagickFalse)
618 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
619 image->filename);
620 }
621 if (image->debug != MagickFalse)
622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000623 "Profile: iptc, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000624 return(MagickTrue);
625}
626
627static boolean ReadProfile(j_decompress_ptr jpeg_info)
628{
629 char
630 name[MaxTextExtent];
631
cristye23ec9d2011-08-16 18:15:40 +0000632 const StringInfo
633 *previous_profile;
634
cristy3ed852e2009-09-05 21:47:34 +0000635 ErrorManager
636 *error_manager;
637
cristy018f07f2011-09-04 21:15:19 +0000638 ExceptionInfo
639 *exception;
640
cristy3ed852e2009-09-05 21:47:34 +0000641 Image
642 *image;
643
644 int
645 marker;
646
647 MagickBooleanType
648 status;
649
cristybb503372010-05-27 20:51:26 +0000650 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000651 i;
652
653 register unsigned char
654 *p;
655
656 size_t
657 length;
658
659 StringInfo
660 *profile;
661
662 /*
663 Read generic profile.
664 */
cristybb503372010-05-27 20:51:26 +0000665 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000666 length+=(size_t) GetCharacter(jpeg_info);
667 if (length <= 2)
668 return(MagickTrue);
669 length-=2;
670 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000671 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000672 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000673 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000674 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000675 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000676 if (profile == (StringInfo *) NULL)
677 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
678 image->filename);
cristyf551aee2012-09-27 21:45:23 +0000679 error_manager->profile=profile;
cristy3ed852e2009-09-05 21:47:34 +0000680 p=GetStringInfoDatum(profile);
cristy08f255a2011-08-17 01:23:06 +0000681 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000682 *p++=(unsigned char) GetCharacter(jpeg_info);
cristyf551aee2012-09-27 21:45:23 +0000683 error_manager->profile=NULL;
cristy3ed852e2009-09-05 21:47:34 +0000684 if (marker == 1)
685 {
686 p=GetStringInfoDatum(profile);
687 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
688 (void) CopyMagickString(name,"exif",MaxTextExtent);
689 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
690 {
cristybb503372010-05-27 20:51:26 +0000691 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000692 j;
693
694 /*
695 Extract namespace from XMP profile.
696 */
697 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000698 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000699 {
700 if (*p == '\0')
701 break;
702 p++;
703 }
cristybb503372010-05-27 20:51:26 +0000704 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000705 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
706 (void) CopyMagickString(name,"xmp",MaxTextExtent);
707 }
708 }
cristye23ec9d2011-08-16 18:15:40 +0000709 previous_profile=GetImageProfile(image,name);
710 if (previous_profile != (const StringInfo *) NULL)
cristy08f255a2011-08-17 01:23:06 +0000711 {
cristy1b58f252012-03-01 01:41:41 +0000712 size_t
cristy05c0c9a2011-09-05 23:16:13 +0000713 length;
714
715 length=GetStringInfoLength(profile);
cristy08f255a2011-08-17 01:23:06 +0000716 SetStringInfoLength(profile,GetStringInfoLength(profile)+
717 GetStringInfoLength(previous_profile));
cristy83ab3d82011-09-06 12:05:51 +0000718 (void) memmove(GetStringInfoDatum(profile)+
719 GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
720 length);
cristy08f255a2011-08-17 01:23:06 +0000721 (void) memcpy(GetStringInfoDatum(profile),
722 GetStringInfoDatum(previous_profile),
723 GetStringInfoLength(previous_profile));
724 }
cristyd15e6592011-10-15 00:13:06 +0000725 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000726 profile=DestroyStringInfo(profile);
727 if (status == MagickFalse)
728 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
729 image->filename);
730 if (image->debug != MagickFalse)
731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000732 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000733 return(MagickTrue);
734}
735
cristyf2faecf2010-05-28 19:19:36 +0000736static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000737{
738 SourceManager
739 *source;
740
741 if (number_bytes <= 0)
742 return;
743 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000744 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000745 {
cristyf2faecf2010-05-28 19:19:36 +0000746 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000747 (void) FillInputBuffer(cinfo);
748 }
cristy4cb162a2010-05-30 03:04:47 +0000749 source->manager.next_input_byte+=number_bytes;
750 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000751}
752
753static void TerminateSource(j_decompress_ptr cinfo)
754{
cristydf0d90e2011-12-12 01:03:55 +0000755 (void) cinfo;
cristy3ed852e2009-09-05 21:47:34 +0000756}
757
758static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
759{
760 SourceManager
761 *source;
762
763 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
764 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
765 source=(SourceManager *) cinfo->src;
766 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
767 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
768 source=(SourceManager *) cinfo->src;
769 source->manager.init_source=InitializeSource;
770 source->manager.fill_input_buffer=FillInputBuffer;
771 source->manager.skip_input_data=SkipInputData;
772 source->manager.resync_to_restart=jpeg_resync_to_restart;
773 source->manager.term_source=TerminateSource;
774 source->manager.bytes_in_buffer=0;
775 source->manager.next_input_byte=NULL;
776 source->image=image;
777}
778
779static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
dirk881355a2013-08-19 19:56:50 +0000780 Image *image, ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000781{
782 image->quality=UndefinedCompressionQuality;
783#if defined(D_PROGRESSIVE_SUPPORTED)
784 if (image->compression == LosslessJPEGCompression)
785 {
dirk29dd80e2013-10-31 23:11:11 +0000786 image->quality=100;
cristy3ed852e2009-09-05 21:47:34 +0000787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
788 "Quality: 100 (lossless)");
789 }
790 else
791#endif
792 {
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 j,
795 qvalue,
796 sum;
797
cristybb503372010-05-27 20:51:26 +0000798 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000799 i;
800
801 /*
802 Determine the JPEG compression quality from the quantization tables.
803 */
804 sum=0;
805 for (i=0; i < NUM_QUANT_TBLS; i++)
806 {
807 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
808 for (j=0; j < DCTSIZE2; j++)
809 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
dirk881355a2013-08-19 19:56:50 +0000810 }
811 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
812 (jpeg_info->quant_tbl_ptrs[1] != NULL))
813 {
814 ssize_t
815 hash[101] =
816 {
817 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
818 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
819 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
820 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
821 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
822 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
823 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
824 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
825 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
826 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
827 0
828 },
829 sums[101] =
830 {
831 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
832 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
833 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
834 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
835 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
836 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
837 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
838 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
839 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
840 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
841 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
842 128, 0
843 };
844
845 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
846 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
847 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
848 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
849 for (i=0; i < 100; i++)
850 {
851 if ((qvalue < hash[i]) && (sum < sums[i]))
852 continue;
853 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000854 image->quality=(size_t) i+1;
dirk881355a2013-08-19 19:56:50 +0000855 if (image->debug != MagickFalse)
856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
857 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
858 (sum <= sums[i]) ? "exact" : "approximate");
859 break;
860 }
861 }
862 else
863 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
864 {
865 ssize_t
866 hash[101] =
867 {
868 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
869 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
870 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
871 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
872 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
873 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
874 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
875 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
876 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
877 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
cristy3ed852e2009-09-05 21:47:34 +0000878 0
dirk881355a2013-08-19 19:56:50 +0000879 },
880 sums[101] =
881 {
882 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
883 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
884 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
885 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
886 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
887 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
888 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
889 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
890 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
891 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
892 667, 592, 518, 441, 369, 292, 221, 151, 86,
893 64, 0
894 };
cristy3ed852e2009-09-05 21:47:34 +0000895
dirk881355a2013-08-19 19:56:50 +0000896 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
897 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
898 for (i=0; i < 100; i++)
899 {
900 if ((qvalue < hash[i]) && (sum < sums[i]))
901 continue;
902 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
dirk29dd80e2013-10-31 23:11:11 +0000903 image->quality=(size_t)i+1;
dirk881355a2013-08-19 19:56:50 +0000904 if (image->debug != MagickFalse)
905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
906 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
907 (sum <= sums[i]) ? "exact" : "approximate");
908 break;
909 }
910 }
cristy3ed852e2009-09-05 21:47:34 +0000911 }
912}
913
cristyd15e6592011-10-15 00:13:06 +0000914static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000915{
916 char
917 sampling_factor[MaxTextExtent];
918
919 switch (jpeg_info->out_color_space)
920 {
921 case JCS_CMYK:
922 {
923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000924 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000925 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
926 jpeg_info->comp_info[0].v_samp_factor,
927 jpeg_info->comp_info[1].h_samp_factor,
928 jpeg_info->comp_info[1].v_samp_factor,
929 jpeg_info->comp_info[2].h_samp_factor,
930 jpeg_info->comp_info[2].v_samp_factor,
931 jpeg_info->comp_info[3].h_samp_factor,
932 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000933 break;
cristy3ed852e2009-09-05 21:47:34 +0000934 }
935 case JCS_GRAYSCALE:
936 {
937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
938 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000939 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000940 jpeg_info->comp_info[0].h_samp_factor,
941 jpeg_info->comp_info[0].v_samp_factor);
942 break;
943 }
944 case JCS_RGB:
945 {
946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000947 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000948 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
949 jpeg_info->comp_info[0].v_samp_factor,
950 jpeg_info->comp_info[1].h_samp_factor,
951 jpeg_info->comp_info[1].v_samp_factor,
952 jpeg_info->comp_info[2].h_samp_factor,
953 jpeg_info->comp_info[2].v_samp_factor);
954 break;
955 }
956 default:
957 {
958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
959 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000960 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000961 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
962 jpeg_info->comp_info[0].v_samp_factor,
963 jpeg_info->comp_info[1].h_samp_factor,
964 jpeg_info->comp_info[1].v_samp_factor,
965 jpeg_info->comp_info[2].h_samp_factor,
966 jpeg_info->comp_info[2].v_samp_factor,
967 jpeg_info->comp_info[3].h_samp_factor,
968 jpeg_info->comp_info[3].v_samp_factor);
969 break;
970 }
971 }
cristyd15e6592011-10-15 00:13:06 +0000972 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
973 exception);
cristye90d7402010-03-14 18:21:29 +0000974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
975 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000976}
977
978static Image *ReadJPEGImage(const ImageInfo *image_info,
979 ExceptionInfo *exception)
980{
981 char
982 value[MaxTextExtent];
983
cristycaec74e2009-09-14 02:20:04 +0000984 const char
cristy11151212009-09-14 13:10:15 +0000985 *option;
cristycaec74e2009-09-14 02:20:04 +0000986
cristy3ed852e2009-09-05 21:47:34 +0000987 ErrorManager
988 error_manager;
989
cristy3ed852e2009-09-05 21:47:34 +0000990 Image
991 *image;
992
cristy3ed852e2009-09-05 21:47:34 +0000993 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +0000994 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000995
996 JSAMPROW
997 scanline[1];
998
999 MagickBooleanType
1000 debug,
1001 status;
1002
1003 MagickSizeType
1004 number_pixels;
1005
cristy22646e22013-06-23 16:34:03 +00001006 MemoryInfo
1007 *memory_info;
1008
cristy4c08aed2011-07-01 19:47:50 +00001009 Quantum
1010 index;
1011
cristybb503372010-05-27 20:51:26 +00001012 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001013 i;
1014
1015 struct jpeg_decompress_struct
1016 jpeg_info;
1017
1018 struct jpeg_error_mgr
1019 jpeg_error;
1020
1021 register JSAMPLE
1022 *p;
1023
cristybb503372010-05-27 20:51:26 +00001024 size_t
cristy3ed852e2009-09-05 21:47:34 +00001025 precision,
1026 units;
1027
cristy524222d2011-04-25 00:37:06 +00001028 ssize_t
1029 y;
1030
cristy3ed852e2009-09-05 21:47:34 +00001031 /*
1032 Open image file.
1033 */
1034 assert(image_info != (const ImageInfo *) NULL);
1035 assert(image_info->signature == MagickSignature);
1036 if (image_info->debug != MagickFalse)
1037 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1038 image_info->filename);
1039 assert(exception != (ExceptionInfo *) NULL);
1040 assert(exception->signature == MagickSignature);
1041 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +00001042 (void) debug;
cristy9950d572011-10-01 18:22:35 +00001043 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001044 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1045 if (status == MagickFalse)
1046 {
1047 image=DestroyImageList(image);
1048 return((Image *) NULL);
1049 }
1050 /*
1051 Initialize JPEG parameters.
1052 */
cristy91044972011-04-22 14:21:16 +00001053 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001054 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1055 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1056 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001057 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +00001058 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy72f87f82013-06-23 16:43:05 +00001059 memory_info=(MemoryInfo *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001060 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001061 error_manager.image=image;
1062 if (setjmp(error_manager.error_recovery) != 0)
1063 {
1064 jpeg_destroy_decompress(&jpeg_info);
cristyf551aee2012-09-27 21:45:23 +00001065 if (error_manager.profile != (StringInfo *) NULL)
1066 error_manager.profile=DestroyStringInfo(error_manager.profile);
cristy3ed852e2009-09-05 21:47:34 +00001067 (void) CloseBlob(image);
1068 number_pixels=(MagickSizeType) image->columns*image->rows;
1069 if (number_pixels != 0)
1070 return(GetFirstImageInList(image));
1071 return(DestroyImage(image));
1072 }
1073 jpeg_info.client_data=(void *) &error_manager;
1074 jpeg_create_decompress(&jpeg_info);
1075 JPEGSourceManager(&jpeg_info,image);
1076 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
1077 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
1078 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1079 for (i=1; i < 16; i++)
1080 if ((i != 2) && (i != 13) && (i != 14))
1081 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +00001082 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +00001083 if ((image_info->colorspace == YCbCrColorspace) ||
1084 (image_info->colorspace == Rec601YCbCrColorspace) ||
1085 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001086 jpeg_info.out_color_space=JCS_YCbCr;
1087 /*
1088 Set image resolution.
1089 */
1090 units=0;
1091 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1092 (jpeg_info.Y_density != 1))
1093 {
cristy2a11bef2011-10-28 18:33:11 +00001094 image->resolution.x=(double) jpeg_info.X_density;
1095 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001096 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001097 }
1098 if (units == 1)
1099 image->units=PixelsPerInchResolution;
1100 if (units == 2)
1101 image->units=PixelsPerCentimeterResolution;
1102 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy092ec8d2013-04-26 13:46:22 +00001103 option=GetImageOption(image_info,"jpeg:size");
cristy11151212009-09-14 13:10:15 +00001104 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001105 {
1106 double
1107 scale_factor;
1108
cristycaec74e2009-09-14 02:20:04 +00001109 GeometryInfo
1110 geometry_info;
1111
cristy0adb4f92009-11-28 18:08:51 +00001112 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001113 flags;
1114
cristy3ed852e2009-09-05 21:47:34 +00001115 /*
cristycaec74e2009-09-14 02:20:04 +00001116 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001117 */
cristy11151212009-09-14 13:10:15 +00001118 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001119 if ((flags & SigmaValue) == 0)
1120 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001121 jpeg_calc_output_dimensions(&jpeg_info);
1122 image->magick_columns=jpeg_info.output_width;
1123 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001124 scale_factor=1.0;
1125 if (geometry_info.rho != 0.0)
1126 scale_factor=jpeg_info.output_width/geometry_info.rho;
1127 if ((geometry_info.sigma != 0.0) &&
1128 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1129 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001130 jpeg_info.scale_num=1U;
1131 jpeg_info.scale_denom=(unsigned int) scale_factor;
1132 jpeg_calc_output_dimensions(&jpeg_info);
1133 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1135 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001136 }
cristybb503372010-05-27 20:51:26 +00001137 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001138#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1139#if defined(D_LOSSLESS_SUPPORTED)
1140 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1141 JPEGInterlace : NoInterlace;
1142 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1143 LosslessJPEGCompression : JPEGCompression;
1144 if (jpeg_info.data_precision > 8)
1145 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1146 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1147 image->filename);
1148 if (jpeg_info.data_precision == 16)
1149 jpeg_info.data_precision=12;
1150#else
1151 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1152 NoInterlace;
1153 image->compression=JPEGCompression;
1154#endif
1155#else
1156 image->compression=JPEGCompression;
1157 image->interlace=JPEGInterlace;
1158#endif
cristy092ec8d2013-04-26 13:46:22 +00001159 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001160 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001161 {
cristyb4bb39c2012-02-21 18:45:54 +00001162 /*
1163 Let the JPEG library quantize the image.
1164 */
cristy3ed852e2009-09-05 21:47:34 +00001165 jpeg_info.quantize_colors=MagickTrue;
anthony41906552011-10-07 12:15:27 +00001166 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001167 }
cristy092ec8d2013-04-26 13:46:22 +00001168 option=GetImageOption(image_info,"jpeg:block-smoothing");
cristy0d737182013-04-04 00:22:57 +00001169 if (option != (const char *) NULL)
1170 jpeg_info.do_block_smoothing=IsStringTrue(option);
cristyb4bb39c2012-02-21 18:45:54 +00001171 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00001172 option=GetImageOption(image_info,"jpeg:dct-method");
cristy787d4352010-03-06 13:55:58 +00001173 if (option != (const char *) NULL)
1174 switch (*option)
1175 {
1176 case 'D':
1177 case 'd':
1178 {
1179 if (LocaleCompare(option,"default") == 0)
1180 jpeg_info.dct_method=JDCT_DEFAULT;
1181 break;
1182 }
1183 case 'F':
1184 case 'f':
1185 {
1186 if (LocaleCompare(option,"fastest") == 0)
1187 jpeg_info.dct_method=JDCT_FASTEST;
1188 if (LocaleCompare(option,"float") == 0)
1189 jpeg_info.dct_method=JDCT_FLOAT;
1190 break;
1191 }
1192 case 'I':
1193 case 'i':
1194 {
1195 if (LocaleCompare(option,"ifast") == 0)
1196 jpeg_info.dct_method=JDCT_IFAST;
1197 if (LocaleCompare(option,"islow") == 0)
1198 jpeg_info.dct_method=JDCT_ISLOW;
1199 break;
1200 }
1201 }
cristy092ec8d2013-04-26 13:46:22 +00001202 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
cristy0d737182013-04-04 00:22:57 +00001203 if (option != (const char *) NULL)
1204 jpeg_info.do_fancy_upsampling=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00001205 (void) jpeg_start_decompress(&jpeg_info);
1206 image->columns=jpeg_info.output_width;
1207 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001208 image->depth=(size_t) jpeg_info.data_precision;
cristy93ff2cc2012-05-13 21:03:53 +00001209 switch (jpeg_info.out_color_space)
1210 {
1211 case JCS_RGB:
1212 default:
1213 {
cristyf551aee2012-09-27 21:45:23 +00001214 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001215 break;
1216 }
1217 case JCS_GRAYSCALE:
1218 {
cristy1a45be72013-04-07 00:16:24 +00001219 (void) SetImageColorspace(image,GRAYColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001220 break;
1221 }
1222 case JCS_YCbCr:
1223 {
cristyf551aee2012-09-27 21:45:23 +00001224 (void) SetImageColorspace(image,YCbCrColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001225 break;
1226 }
1227 case JCS_CMYK:
1228 {
cristyf551aee2012-09-27 21:45:23 +00001229 (void) SetImageColorspace(image,CMYKColorspace,exception);
cristy93ff2cc2012-05-13 21:03:53 +00001230 break;
1231 }
1232 }
cristy36fc5502012-05-22 11:32:23 +00001233 if (IsITUFaxImage(image) != MagickFalse)
1234 {
cristyf551aee2012-09-27 21:45:23 +00001235 (void) SetImageColorspace(image,LabColorspace,exception);
cristy36fc5502012-05-22 11:32:23 +00001236 jpeg_info.out_color_space=JCS_YCbCr;
1237 }
cristy092ec8d2013-04-26 13:46:22 +00001238 option=GetImageOption(image_info,"jpeg:colors");
anthony41906552011-10-07 12:15:27 +00001239 if (option != (const char *) NULL)
1240 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1241 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001242 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1243 if ((jpeg_info.output_components == 1) &&
1244 (jpeg_info.quantize_colors == MagickFalse))
1245 {
cristybb503372010-05-27 20:51:26 +00001246 size_t
cristy3ed852e2009-09-05 21:47:34 +00001247 colors;
1248
cristybb503372010-05-27 20:51:26 +00001249 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001250 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001251 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1252 }
1253 if (image->debug != MagickFalse)
1254 {
1255 if (image->interlace != NoInterlace)
1256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1257 "Interlace: progressive");
1258 else
1259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1260 "Interlace: nonprogressive");
1261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1262 (int) jpeg_info.data_precision);
1263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1264 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1265 }
dirk881355a2013-08-19 19:56:50 +00001266 JPEGSetImageQuality(&jpeg_info,image,exception);
cristyd15e6592011-10-15 00:13:06 +00001267 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristyb51dff52011-05-19 16:55:47 +00001268 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001269 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001270 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001271 if (image_info->ping != MagickFalse)
1272 {
1273 jpeg_destroy_decompress(&jpeg_info);
1274 (void) CloseBlob(image);
1275 return(GetFirstImageInList(image));
1276 }
cristy22646e22013-06-23 16:34:03 +00001277 memory_info=AcquireVirtualMemory((size_t) image->columns,
1278 jpeg_info.output_components*sizeof(*jpeg_pixels));
1279 if (memory_info == (MemoryInfo *) NULL)
cristyee1bdaa2013-07-16 16:45:03 +00001280 {
1281 jpeg_destroy_decompress(&jpeg_info);
1282 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1283 }
cristy22646e22013-06-23 16:34:03 +00001284 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001285 /*
1286 Convert JPEG pixels to pixel packets.
1287 */
1288 if (setjmp(error_manager.error_recovery) != 0)
1289 {
cristy22646e22013-06-23 16:34:03 +00001290 if (memory_info != (MemoryInfo *) NULL)
1291 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001292 jpeg_destroy_decompress(&jpeg_info);
1293 (void) CloseBlob(image);
1294 number_pixels=(MagickSizeType) image->columns*image->rows;
1295 if (number_pixels != 0)
1296 return(GetFirstImageInList(image));
1297 return(DestroyImage(image));
1298 }
1299 if (jpeg_info.quantize_colors != MagickFalse)
1300 {
cristybb503372010-05-27 20:51:26 +00001301 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001302 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001303 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001304 {
cristy1b58f252012-03-01 01:41:41 +00001305 image->colormap[i].red=(double) ScaleCharToQuantum(
1306 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001307 image->colormap[i].green=image->colormap[i].red;
1308 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001309 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001310 }
1311 else
cristybb503372010-05-27 20:51:26 +00001312 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001313 {
cristy1b58f252012-03-01 01:41:41 +00001314 image->colormap[i].red=(double) ScaleCharToQuantum(
1315 jpeg_info.colormap[0][i]);
1316 image->colormap[i].green=(double) ScaleCharToQuantum(
1317 jpeg_info.colormap[1][i]);
1318 image->colormap[i].blue=(double) ScaleCharToQuantum(
1319 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001320 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001321 }
1322 }
1323 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001324 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001325 {
cristybb503372010-05-27 20:51:26 +00001326 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001327 x;
1328
cristy4c08aed2011-07-01 19:47:50 +00001329 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001330 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001331
1332 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1333 {
1334 (void) ThrowMagickException(exception,GetMagickModule(),
1335 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1336 continue;
1337 }
1338 p=jpeg_pixels;
1339 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001340 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001341 break;
cristy3ed852e2009-09-05 21:47:34 +00001342 if (jpeg_info.data_precision > 8)
1343 {
1344 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001345 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001346 {
cristybb503372010-05-27 20:51:26 +00001347 size_t
cristy3ed852e2009-09-05 21:47:34 +00001348 pixel;
1349
1350 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001351 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001352 else
cristybb503372010-05-27 20:51:26 +00001353 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristyc82a27b2011-10-21 01:07:16 +00001354 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001355 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001356 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001357 p++;
cristyed231572011-07-14 02:18:59 +00001358 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001359 }
1360 else
1361 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001362 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001363 {
cristy4c08aed2011-07-01 19:47:50 +00001364 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1365 (GETJSAMPLE(*p++) << 4)),q);
1366 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1367 (GETJSAMPLE(*p++) << 4)),q);
1368 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1369 (GETJSAMPLE(*p++) << 4)),q);
1370 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001371 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001372 }
1373 else
cristybb503372010-05-27 20:51:26 +00001374 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001375 {
cristy4c08aed2011-07-01 19:47:50 +00001376 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1377 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1378 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1379 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1380 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1381 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1382 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1383 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1384 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001385 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001386 }
1387 }
1388 else
1389 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001390 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001391 {
cristyc82a27b2011-10-21 01:07:16 +00001392 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001393 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001394 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001395 p++;
cristyed231572011-07-14 02:18:59 +00001396 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001397 }
1398 else
1399 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001400 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001401 {
cristy4c08aed2011-07-01 19:47:50 +00001402 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1403 GETJSAMPLE(*p++)),q);
1404 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1405 GETJSAMPLE(*p++)),q);
1406 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1407 GETJSAMPLE(*p++)),q);
1408 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001409 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001410 }
1411 else
cristybb503372010-05-27 20:51:26 +00001412 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001413 {
cristy4c08aed2011-07-01 19:47:50 +00001414 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1415 (unsigned char) GETJSAMPLE(*p++)),q);
1416 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1417 (unsigned char) GETJSAMPLE(*p++)),q);
1418 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1419 (unsigned char) GETJSAMPLE(*p++)),q);
1420 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1421 (unsigned char) GETJSAMPLE(*p++)),q);
1422 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001423 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001424 }
1425 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1426 break;
cristy524222d2011-04-25 00:37:06 +00001427 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1428 image->rows);
1429 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001430 {
1431 jpeg_abort_decompress(&jpeg_info);
1432 break;
1433 }
cristy3ed852e2009-09-05 21:47:34 +00001434 }
cristyd28b1dd2011-05-14 20:30:38 +00001435 if (status != MagickFalse)
1436 {
1437 error_manager.finished=MagickTrue;
1438 if (setjmp(error_manager.error_recovery) == 0)
1439 (void) jpeg_finish_decompress(&jpeg_info);
1440 }
cristy3ed852e2009-09-05 21:47:34 +00001441 /*
1442 Free jpeg resources.
1443 */
cristy3ed852e2009-09-05 21:47:34 +00001444 jpeg_destroy_decompress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00001445 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00001446 (void) CloseBlob(image);
1447 return(GetFirstImageInList(image));
1448}
1449#endif
dirk29dd80e2013-10-31 23:11:11 +00001450
1451
cristy3ed852e2009-09-05 21:47:34 +00001452/*
1453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1454% %
1455% %
1456% %
1457% R e g i s t e r J P E G I m a g e %
1458% %
1459% %
1460% %
1461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462%
1463% RegisterJPEGImage() adds properties for the JPEG image format to
1464% the list of supported formats. The properties include the image format
1465% tag, a method to read and/or write the format, whether the format
1466% supports the saving of more than one frame to the same file or blob,
1467% whether the format supports native in-memory I/O, and a brief
1468% description of the format.
1469%
1470% The format of the RegisterJPEGImage method is:
1471%
cristybb503372010-05-27 20:51:26 +00001472% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001473%
1474*/
cristybb503372010-05-27 20:51:26 +00001475ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001476{
1477 char
1478 version[MaxTextExtent];
1479
1480 MagickInfo
1481 *entry;
1482
1483 static const char
cristy7138c592009-09-08 13:58:52 +00001484 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001485
1486 *version='\0';
1487#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001488 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001489#endif
1490 entry=SetMagickInfo("JPEG");
1491 entry->thread_support=NoThreadSupport;
1492#if defined(MAGICKCORE_JPEG_DELEGATE)
1493 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1494 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1495#endif
1496 entry->magick=(IsImageFormatHandler *) IsJPEG;
1497 entry->adjoin=MagickFalse;
1498 entry->description=ConstantString(description);
1499 if (*version != '\0')
1500 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001501 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001502 entry->module=ConstantString("JPEG");
1503 (void) RegisterMagickInfo(entry);
1504 entry=SetMagickInfo("JPG");
1505 entry->thread_support=NoThreadSupport;
1506#if defined(MAGICKCORE_JPEG_DELEGATE)
1507 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1508 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1509#endif
1510 entry->adjoin=MagickFalse;
1511 entry->description=ConstantString(description);
1512 if (*version != '\0')
1513 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001514 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001515 entry->module=ConstantString("JPEG");
1516 (void) RegisterMagickInfo(entry);
1517 entry=SetMagickInfo("PJPEG");
1518 entry->thread_support=NoThreadSupport;
1519#if defined(MAGICKCORE_JPEG_DELEGATE)
1520 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1521 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1522#endif
1523 entry->adjoin=MagickFalse;
1524 entry->description=ConstantString(description);
1525 if (*version != '\0')
1526 entry->version=ConstantString(version);
cristy4a6ab482013-08-09 01:29:35 +00001527 entry->mime_type=ConstantString("image/jpeg");
cristy3ed852e2009-09-05 21:47:34 +00001528 entry->module=ConstantString("JPEG");
1529 (void) RegisterMagickInfo(entry);
1530 return(MagickImageCoderSignature);
1531}
dirk29dd80e2013-10-31 23:11:11 +00001532
1533
cristy3ed852e2009-09-05 21:47:34 +00001534/*
1535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1536% %
1537% %
1538% %
1539% U n r e g i s t e r J P E G I m a g e %
1540% %
1541% %
1542% %
1543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1544%
1545% UnregisterJPEGImage() removes format registrations made by the
1546% JPEG module from the list of supported formats.
1547%
1548% The format of the UnregisterJPEGImage method is:
1549%
1550% UnregisterJPEGImage(void)
1551%
1552*/
1553ModuleExport void UnregisterJPEGImage(void)
1554{
1555 (void) UnregisterMagickInfo("PJPG");
1556 (void) UnregisterMagickInfo("JPEG");
1557 (void) UnregisterMagickInfo("JPG");
1558}
dirk29dd80e2013-10-31 23:11:11 +00001559
1560
cristy3ed852e2009-09-05 21:47:34 +00001561#if defined(MAGICKCORE_JPEG_DELEGATE)
1562/*
1563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1564% %
1565% %
1566% %
1567% W r i t e J P E G I m a g e %
1568% %
1569% %
1570% %
1571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1572%
1573% WriteJPEGImage() writes a JPEG image file and returns it. It
1574% allocates the memory necessary for the new Image structure and returns a
1575% pointer to the new image.
1576%
1577% The format of the WriteJPEGImage method is:
1578%
cristy91044972011-04-22 14:21:16 +00001579% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001580% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001581%
1582% A description of each parameter follows:
1583%
1584% o image_info: the image info.
1585%
1586% o jpeg_image: The image.
1587%
cristy1e178e72011-08-28 19:44:34 +00001588% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001589%
1590*/
1591
cristy1b58f252012-03-01 01:41:41 +00001592static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1593{
1594 assert(table != (QuantizationTable *) NULL);
1595 if (table->slot != (char *) NULL)
1596 table->slot=DestroyString(table->slot);
1597 if (table->description != (char *) NULL)
1598 table->description=DestroyString(table->description);
1599 if (table->levels != (unsigned int *) NULL)
1600 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1601 table=(QuantizationTable *) RelinquishMagickMemory(table);
1602 return(table);
1603}
1604
cristy3ed852e2009-09-05 21:47:34 +00001605static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1606{
1607 DestinationManager
1608 *destination;
1609
1610 destination=(DestinationManager *) cinfo->dest;
1611 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1612 MaxBufferExtent,destination->buffer);
1613 if (destination->manager.free_in_buffer != MaxBufferExtent)
1614 ERREXIT(cinfo,JERR_FILE_WRITE);
1615 destination->manager.next_output_byte=destination->buffer;
1616 return(TRUE);
1617}
1618
cristy1b58f252012-03-01 01:41:41 +00001619static QuantizationTable *GetQuantizationTable(const char *filename,
1620 const char *slot,ExceptionInfo *exception)
1621{
1622 char
1623 *p,
1624 *xml;
1625
1626 const char
1627 *attribute,
1628 *content;
1629
1630 double
1631 value;
1632
1633 register ssize_t
1634 i;
1635
1636 ssize_t
1637 j;
1638
1639 QuantizationTable
1640 *table;
1641
cristy1fcc8b62012-03-02 17:25:55 +00001642 size_t
1643 length;
1644
cristy1b58f252012-03-01 01:41:41 +00001645 XMLTreeInfo
1646 *description,
1647 *levels,
1648 *quantization_tables,
1649 *table_iterator;
1650
1651 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1652 "Loading quantization tables \"%s\" ...",filename);
1653 table=(QuantizationTable *) NULL;
cristy3a5987c2013-11-07 14:18:46 +00001654 xml=FileToString(filename,~0UL,exception);
cristy1b58f252012-03-01 01:41:41 +00001655 if (xml == (char *) NULL)
1656 return(table);
1657 quantization_tables=NewXMLTree(xml,exception);
1658 if (quantization_tables == (XMLTreeInfo *) NULL)
1659 {
1660 xml=DestroyString(xml);
1661 return(table);
1662 }
1663 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1664 table_iterator != (XMLTreeInfo *) NULL;
1665 table_iterator=GetNextXMLTreeTag(table_iterator))
1666 {
1667 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1668 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1669 break;
1670 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1671 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1672 break;
1673 }
1674 if (table_iterator == (XMLTreeInfo *) NULL)
1675 {
1676 xml=DestroyString(xml);
1677 return(table);
1678 }
1679 description=GetXMLTreeChild(table_iterator,"description");
1680 if (description == (XMLTreeInfo *) NULL)
1681 {
1682 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1683 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1684 quantization_tables=DestroyXMLTree(quantization_tables);
1685 xml=DestroyString(xml);
1686 return(table);
1687 }
1688 levels=GetXMLTreeChild(table_iterator,"levels");
1689 if (levels == (XMLTreeInfo *) NULL)
1690 {
1691 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1692 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1693 quantization_tables=DestroyXMLTree(quantization_tables);
1694 xml=DestroyString(xml);
1695 return(table);
1696 }
1697 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1698 if (table == (QuantizationTable *) NULL)
1699 ThrowFatalException(ResourceLimitFatalError,
1700 "UnableToAcquireQuantizationTable");
1701 table->slot=(char *) NULL;
1702 table->description=(char *) NULL;
1703 table->levels=(unsigned int *) NULL;
1704 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1705 if (attribute != (char *) NULL)
1706 table->slot=ConstantString(attribute);
1707 content=GetXMLTreeContent(description);
1708 if (content != (char *) NULL)
1709 table->description=ConstantString(content);
1710 attribute=GetXMLTreeAttribute(levels,"width");
1711 if (attribute == (char *) NULL)
1712 {
1713 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1714 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1715 quantization_tables=DestroyXMLTree(quantization_tables);
1716 table=DestroyQuantizationTable(table);
1717 xml=DestroyString(xml);
1718 return(table);
1719 }
1720 table->width=StringToUnsignedLong(attribute);
1721 if (table->width == 0)
1722 {
1723 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1724 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1725 quantization_tables=DestroyXMLTree(quantization_tables);
1726 table=DestroyQuantizationTable(table);
1727 xml=DestroyString(xml);
1728 return(table);
1729 }
1730 attribute=GetXMLTreeAttribute(levels,"height");
1731 if (attribute == (char *) NULL)
1732 {
1733 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1734 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1735 quantization_tables=DestroyXMLTree(quantization_tables);
1736 table=DestroyQuantizationTable(table);
1737 xml=DestroyString(xml);
1738 return(table);
1739 }
1740 table->height=StringToUnsignedLong(attribute);
1741 if (table->height == 0)
1742 {
1743 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1744 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1745 quantization_tables=DestroyXMLTree(quantization_tables);
1746 table=DestroyQuantizationTable(table);
1747 xml=DestroyString(xml);
1748 return(table);
1749 }
1750 attribute=GetXMLTreeAttribute(levels,"divisor");
1751 if (attribute == (char *) NULL)
1752 {
1753 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1754 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1755 quantization_tables=DestroyXMLTree(quantization_tables);
1756 table=DestroyQuantizationTable(table);
1757 xml=DestroyString(xml);
1758 return(table);
1759 }
cristy043f3f32012-03-02 17:37:28 +00001760 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1761 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001762 {
1763 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1764 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1765 quantization_tables=DestroyXMLTree(quantization_tables);
1766 table=DestroyQuantizationTable(table);
1767 xml=DestroyString(xml);
1768 return(table);
1769 }
1770 content=GetXMLTreeContent(levels);
1771 if (content == (char *) NULL)
1772 {
1773 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1774 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1775 quantization_tables=DestroyXMLTree(quantization_tables);
1776 table=DestroyQuantizationTable(table);
1777 xml=DestroyString(xml);
1778 return(table);
1779 }
cristy1fcc8b62012-03-02 17:25:55 +00001780 length=(size_t) table->width*table->height;
1781 if (length < 64)
1782 length=64;
cristy092006b2012-03-02 17:26:02 +00001783 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001784 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001785 if (table->levels == (unsigned int *) NULL)
1786 ThrowFatalException(ResourceLimitFatalError,
1787 "UnableToAcquireQuantizationTable");
1788 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1789 {
cristy043f3f32012-03-02 17:37:28 +00001790 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1791 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001792 while (isspace((int) ((unsigned char) *p)) != 0)
1793 p++;
1794 if (*p == ',')
1795 p++;
1796 content=p;
1797 }
cristy043f3f32012-03-02 17:37:28 +00001798 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001799 (void) value;
1800 if (p != content)
1801 {
1802 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1803 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1804 quantization_tables=DestroyXMLTree(quantization_tables);
1805 table=DestroyQuantizationTable(table);
1806 xml=DestroyString(xml);
1807 return(table);
1808 }
cristy043f3f32012-03-02 17:37:28 +00001809 for (j=i; j < 64; j++)
1810 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001811 quantization_tables=DestroyXMLTree(quantization_tables);
1812 xml=DestroyString(xml);
1813 return(table);
1814}
1815
cristy3ed852e2009-09-05 21:47:34 +00001816static void InitializeDestination(j_compress_ptr cinfo)
1817{
1818 DestinationManager
1819 *destination;
1820
1821 destination=(DestinationManager *) cinfo->dest;
1822 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1823 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1824 destination->manager.next_output_byte=destination->buffer;
1825 destination->manager.free_in_buffer=MaxBufferExtent;
1826}
1827
1828static inline size_t MagickMin(const size_t x,const size_t y)
1829{
1830 if (x < y)
1831 return(x);
1832 return(y);
1833}
1834
1835static void TerminateDestination(j_compress_ptr cinfo)
1836{
1837 DestinationManager
1838 *destination;
1839
1840 destination=(DestinationManager *) cinfo->dest;
1841 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1842 {
1843 ssize_t
1844 count;
1845
1846 count=WriteBlob(destination->image,MaxBufferExtent-
1847 destination->manager.free_in_buffer,destination->buffer);
1848 if (count != (ssize_t)
1849 (MaxBufferExtent-destination->manager.free_in_buffer))
1850 ERREXIT(cinfo,JERR_FILE_WRITE);
1851 }
1852}
1853
1854static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1855{
1856 const char
1857 *name;
1858
1859 const StringInfo
1860 *profile;
1861
1862 MagickBooleanType
1863 iptc;
1864
cristybb503372010-05-27 20:51:26 +00001865 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001866 i;
1867
1868 size_t
cristy524222d2011-04-25 00:37:06 +00001869 length,
1870 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001871
1872 StringInfo
1873 *custom_profile;
1874
cristy3ed852e2009-09-05 21:47:34 +00001875 /*
1876 Save image profile as a APP marker.
1877 */
1878 iptc=MagickFalse;
1879 custom_profile=AcquireStringInfo(65535L);
1880 ResetImageProfileIterator(image);
1881 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1882 {
cristy109e5572010-09-16 18:38:17 +00001883 register unsigned char
1884 *p;
1885
cristy3ed852e2009-09-05 21:47:34 +00001886 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001887 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001888 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1892 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1893 (unsigned int) length);
1894 }
1895 if (LocaleCompare(name,"ICC") == 0)
1896 {
1897 register unsigned char
1898 *p;
1899
cristy8ba82ae2013-05-29 14:19:49 +00001900 tag_length=strlen(ICC_PROFILE);
cristy35ce5c32010-09-16 23:16:02 +00001901 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001902 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristyc3696d42013-07-16 20:18:37 +00001903 p[tag_length]='\0';
cristybb503372010-05-27 20:51:26 +00001904 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001905 {
1906 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001907 p[12]=(unsigned char) ((i/65519L)+1);
1908 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
cristyc1381ed2013-06-28 18:10:23 +00001909 (void) CopyMagickMemory(p+tag_length+3,GetStringInfoDatum(profile)+i,
cristy3ed852e2009-09-05 21:47:34 +00001910 length);
1911 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
cristyc1381ed2013-06-28 18:10:23 +00001912 custom_profile),(unsigned int) (length+tag_length+3));
cristy3ed852e2009-09-05 21:47:34 +00001913 }
1914 }
1915 if (((LocaleCompare(name,"IPTC") == 0) ||
1916 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1917 {
cristybb503372010-05-27 20:51:26 +00001918 size_t
cristy3ed852e2009-09-05 21:47:34 +00001919 roundup;
1920
1921 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001922 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001923 {
1924 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001925 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001926 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1927 {
1928 (void) memcpy(p,"Photoshop 3.0 ",14);
1929 tag_length=14;
1930 }
1931 else
1932 {
1933 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1934 tag_length=26;
1935 p[24]=(unsigned char) (length >> 8);
1936 p[25]=(unsigned char) (length & 0xff);
1937 }
1938 p[13]=0x00;
1939 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001940 if (roundup != 0)
1941 p[length+tag_length]='\0';
1942 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1943 custom_profile),(unsigned int) (length+tag_length+roundup));
1944 }
1945 }
1946 if (LocaleCompare(name,"XMP") == 0)
1947 {
1948 StringInfo
1949 *xmp_profile;
1950
1951 /*
1952 Add namespace to XMP profile.
1953 */
cristy0615f0e2011-10-12 11:36:46 +00001954 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy3ed852e2009-09-05 21:47:34 +00001955 ConcatenateStringInfo(xmp_profile,profile);
1956 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001957 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001958 {
1959 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1960 jpeg_write_marker(jpeg_info,XML_MARKER,
1961 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1962 }
1963 xmp_profile=DestroyStringInfo(xmp_profile);
1964 }
cristye8c25f92010-06-03 00:53:06 +00001965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1966 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001967 name=GetNextImageProfile(image);
1968 }
1969 custom_profile=DestroyStringInfo(custom_profile);
1970}
1971
1972static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1973{
1974 DestinationManager
1975 *destination;
1976
1977 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1978 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1979 destination=(DestinationManager *) cinfo->dest;
1980 destination->manager.init_destination=InitializeDestination;
1981 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1982 destination->manager.term_destination=TerminateDestination;
1983 destination->image=image;
1984}
1985
1986static char **SamplingFactorToList(const char *text)
1987{
1988 char
1989 **textlist;
1990
1991 register char
1992 *q;
1993
1994 register const char
1995 *p;
1996
cristybb503372010-05-27 20:51:26 +00001997 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001998 i;
1999
cristybb503372010-05-27 20:51:26 +00002000 size_t
cristy3ed852e2009-09-05 21:47:34 +00002001 lines;
2002
2003 if (text == (char *) NULL)
2004 return((char **) NULL);
2005 /*
2006 Convert string to an ASCII list.
2007 */
2008 lines=1;
2009 for (p=text; *p != '\0'; p++)
2010 if (*p == ',')
2011 lines++;
2012 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
2013 sizeof(*textlist));
2014 if (textlist == (char **) NULL)
2015 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2016 p=text;
cristybb503372010-05-27 20:51:26 +00002017 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00002018 {
2019 for (q=(char *) p; *q != '\0'; q++)
2020 if (*q == ',')
2021 break;
2022 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
2023 sizeof(*textlist[i]));
2024 if (textlist[i] == (char *) NULL)
2025 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2026 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2027 if (*q == '\r')
2028 q++;
2029 p=q+1;
2030 }
2031 textlist[i]=(char *) NULL;
2032 return(textlist);
2033}
2034
2035static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002036 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002037{
2038 const char
2039 *option,
2040 *sampling_factor,
2041 *value;
2042
2043 ErrorManager
2044 error_manager;
2045
cristy21749e92012-02-18 02:29:21 +00002046 int
cristy7b8ed292013-05-18 01:13:56 +00002047 colorspace,
cristy21749e92012-02-18 02:29:21 +00002048 quality;
2049
cristy3ed852e2009-09-05 21:47:34 +00002050 JSAMPLE
cristy75fc68f2012-10-08 16:26:00 +00002051 *volatile jpeg_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002052
2053 JSAMPROW
2054 scanline[1];
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 MagickBooleanType
2057 status;
2058
cristy22646e22013-06-23 16:34:03 +00002059 MemoryInfo
2060 *memory_info;
2061
cristy3ed852e2009-09-05 21:47:34 +00002062 register JSAMPLE
2063 *q;
2064
cristybb503372010-05-27 20:51:26 +00002065 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002066 i;
2067
cristy524222d2011-04-25 00:37:06 +00002068 ssize_t
2069 y;
2070
cristy3ed852e2009-09-05 21:47:34 +00002071 struct jpeg_compress_struct
2072 jpeg_info;
2073
2074 struct jpeg_error_mgr
2075 jpeg_error;
2076
2077 /*
2078 Open image file.
2079 */
2080 assert(image_info != (const ImageInfo *) NULL);
2081 assert(image_info->signature == MagickSignature);
2082 assert(image != (Image *) NULL);
2083 assert(image->signature == MagickSignature);
2084 if (image->debug != MagickFalse)
2085 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002086 assert(exception != (ExceptionInfo *) NULL);
2087 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00002088 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002089 if (status == MagickFalse)
2090 return(status);
2091 /*
2092 Initialize JPEG parameters.
2093 */
cristy91044972011-04-22 14:21:16 +00002094 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002095 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2096 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2097 jpeg_info.client_data=(void *) image;
2098 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002099 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002100 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002101 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002102 error_manager.image=image;
cristy72f87f82013-06-23 16:43:05 +00002103 memory_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002104 if (setjmp(error_manager.error_recovery) != 0)
2105 {
2106 jpeg_destroy_compress(&jpeg_info);
2107 (void) CloseBlob(image);
2108 return(MagickFalse);
2109 }
2110 jpeg_info.client_data=(void *) &error_manager;
2111 jpeg_create_compress(&jpeg_info);
2112 JPEGDestinationManager(&jpeg_info,image);
2113 if ((image->columns != (unsigned int) image->columns) ||
2114 (image->rows != (unsigned int) image->rows))
2115 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2116 jpeg_info.image_width=(unsigned int) image->columns;
2117 jpeg_info.image_height=(unsigned int) image->rows;
2118 jpeg_info.input_components=3;
2119 jpeg_info.data_precision=8;
2120 jpeg_info.in_color_space=JCS_RGB;
2121 switch (image->colorspace)
2122 {
2123 case CMYKColorspace:
2124 {
2125 jpeg_info.input_components=4;
2126 jpeg_info.in_color_space=JCS_CMYK;
2127 break;
2128 }
2129 case YCbCrColorspace:
2130 case Rec601YCbCrColorspace:
2131 case Rec709YCbCrColorspace:
2132 {
2133 jpeg_info.in_color_space=JCS_YCbCr;
2134 break;
2135 }
2136 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002137 {
2138 jpeg_info.input_components=1;
2139 jpeg_info.in_color_space=JCS_GRAYSCALE;
2140 break;
2141 }
2142 default:
cristy9f396782009-12-21 01:37:45 +00002143 {
cristy3d9f5ba2012-06-26 13:37:31 +00002144 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +00002145 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00002146 break;
cristy9f396782009-12-21 01:37:45 +00002147 }
cristy3ed852e2009-09-05 21:47:34 +00002148 }
2149 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00002150 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002151 {
2152 jpeg_info.input_components=1;
2153 jpeg_info.in_color_space=JCS_GRAYSCALE;
2154 }
2155 jpeg_set_defaults(&jpeg_info);
cristy11369092013-02-03 22:38:57 +00002156 if (jpeg_info.in_color_space == JCS_CMYK)
2157 jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
cristy3ed852e2009-09-05 21:47:34 +00002158 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2159 jpeg_info.data_precision=8;
2160 else
2161 if (sizeof(JSAMPLE) > 1)
2162 jpeg_info.data_precision=12;
2163 jpeg_info.density_unit=(UINT8) 1;
2164 if (image->debug != MagickFalse)
2165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002166 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2167 floor(image->resolution.y+0.5));
2168 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002169 {
2170 /*
2171 Set image resolution.
2172 */
2173 jpeg_info.write_JFIF_header=MagickTrue;
cristy2a11bef2011-10-28 18:33:11 +00002174 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2175 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy0c220632013-09-25 22:10:28 +00002176 /*
2177 Set image resolution units.
2178 */
2179 jpeg_info.density_unit=(UINT8) 0;
cristy3ed852e2009-09-05 21:47:34 +00002180 if (image->units == PixelsPerInchResolution)
2181 jpeg_info.density_unit=(UINT8) 1;
2182 if (image->units == PixelsPerCentimeterResolution)
2183 jpeg_info.density_unit=(UINT8) 2;
2184 }
cristy97cb3bf2012-02-21 18:31:20 +00002185 jpeg_info.dct_method=JDCT_FLOAT;
cristy092ec8d2013-04-26 13:46:22 +00002186 option=GetImageOption(image_info,"jpeg:dct-method");
cristy3ed852e2009-09-05 21:47:34 +00002187 if (option != (const char *) NULL)
2188 switch (*option)
2189 {
2190 case 'D':
2191 case 'd':
2192 {
2193 if (LocaleCompare(option,"default") == 0)
2194 jpeg_info.dct_method=JDCT_DEFAULT;
2195 break;
2196 }
2197 case 'F':
2198 case 'f':
2199 {
2200 if (LocaleCompare(option,"fastest") == 0)
2201 jpeg_info.dct_method=JDCT_FASTEST;
2202 if (LocaleCompare(option,"float") == 0)
2203 jpeg_info.dct_method=JDCT_FLOAT;
2204 break;
2205 }
2206 case 'I':
2207 case 'i':
2208 {
2209 if (LocaleCompare(option,"ifast") == 0)
2210 jpeg_info.dct_method=JDCT_IFAST;
2211 if (LocaleCompare(option,"islow") == 0)
2212 jpeg_info.dct_method=JDCT_ISLOW;
2213 break;
2214 }
2215 }
cristy092ec8d2013-04-26 13:46:22 +00002216 option=GetImageOption(image_info,"jpeg:optimize-coding");
cristy3ed852e2009-09-05 21:47:34 +00002217 if (option != (const char *) NULL)
anthony6f201312012-03-30 04:08:15 +00002218 jpeg_info.optimize_coding=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00002219 else
2220 {
2221 MagickSizeType
2222 length;
2223
2224 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2225 image->rows*sizeof(JSAMPLE);
2226 if (length == (MagickSizeType) ((size_t) length))
2227 {
2228 /*
2229 Perform optimization only if available memory resources permit it.
2230 */
2231 status=AcquireMagickResource(MemoryResource,length);
cristy3ed852e2009-09-05 21:47:34 +00002232 RelinquishMagickResource(MemoryResource,length);
anthony6f201312012-03-30 04:08:15 +00002233 jpeg_info.optimize_coding=status;
cristy3ed852e2009-09-05 21:47:34 +00002234 }
2235 }
2236#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2237 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2238 (image_info->interlace != NoInterlace))
2239 {
2240 if (image->debug != MagickFalse)
2241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2242 "Interlace: progressive");
2243 jpeg_simple_progression(&jpeg_info);
2244 }
2245 else
2246 if (image->debug != MagickFalse)
2247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2248 "Interlace: non-progressive");
2249#else
2250 if (image->debug != MagickFalse)
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2252 "Interlace: nonprogressive");
2253#endif
dirk871abab2013-11-03 13:21:16 +00002254 quality=92;
cristy092ec8d2013-04-26 13:46:22 +00002255 option=GetImageOption(image_info,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002256 if (option != (const char *) NULL)
2257 {
2258 Image
2259 *jpeg_image;
2260
2261 ImageInfo
2262 *jpeg_info;
2263
2264 jpeg_info=CloneImageInfo(image_info);
cristye0823d52013-11-03 21:50:10 +00002265 jpeg_info->blob=NULL;
cristy1e178e72011-08-28 19:44:34 +00002266 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002267 if (jpeg_image != (Image *) NULL)
2268 {
2269 MagickSizeType
2270 extent;
2271
2272 size_t
cristy87e73ab2010-02-12 01:59:12 +00002273 maximum,
2274 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002275
2276 /*
2277 Search for compression quality that does not exceed image extent.
2278 */
2279 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002280 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002281 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
cristy2e5309e2013-04-27 14:48:31 +00002282 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
cristy0adb4f92009-11-28 18:08:51 +00002283 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00002284 maximum=101;
dirkcf845242013-08-11 21:36:19 +00002285 for (minimum=2; minimum < maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002286 {
dirk871abab2013-11-03 13:21:16 +00002287 jpeg_info->quality=minimum+(maximum-minimum+1)/2;
cristy1e178e72011-08-28 19:44:34 +00002288 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002289 if (GetBlobSize(jpeg_image) <= extent)
dirk871abab2013-11-03 13:21:16 +00002290 minimum=jpeg_info->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002291 else
dirk871abab2013-11-03 13:21:16 +00002292 maximum=jpeg_info->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00002293 }
2294 (void) RelinquishUniqueFileResource(jpeg_image->filename);
dirk871abab2013-11-03 13:21:16 +00002295 quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002296 jpeg_image=DestroyImage(jpeg_image);
2297 }
2298 jpeg_info=DestroyImageInfo(jpeg_info);
2299 }
cristy3ed852e2009-09-05 21:47:34 +00002300 if ((image_info->compression != LosslessJPEGCompression) &&
2301 (image->quality <= 100))
2302 {
cristy21749e92012-02-18 02:29:21 +00002303 if (image->quality != UndefinedCompressionQuality)
2304 quality=(int) image->quality;
cristy3ed852e2009-09-05 21:47:34 +00002305 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00002306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2307 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00002308 }
2309 else
2310 {
2311#if !defined(C_LOSSLESS_SUPPORTED)
cristy21749e92012-02-18 02:29:21 +00002312 quality=100;
cristy3ed852e2009-09-05 21:47:34 +00002313 if (image->debug != MagickFalse)
2314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2315#else
2316 if (image->quality < 100)
cristy1e178e72011-08-28 19:44:34 +00002317 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2318 "LosslessToLossyJPEGConversion",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002319 else
2320 {
2321 int
2322 point_transform,
2323 predictor;
2324
2325 predictor=image->quality/100; /* range 1-7 */
2326 point_transform=image->quality % 20; /* range 0-15 */
2327 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2328 if (image->debug != MagickFalse)
2329 {
2330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2331 "Compression: lossless");
2332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2333 "Predictor: %d",predictor);
2334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2335 "Point Transform: %d",point_transform);
2336 }
2337 }
2338#endif
2339 }
cristy21749e92012-02-18 02:29:21 +00002340 jpeg_set_quality(&jpeg_info,quality,MagickTrue);
cristy16e74722012-07-01 13:01:38 +00002341#if (JPEG_LIB_VERSION >= 70)
cristy092ec8d2013-04-26 13:46:22 +00002342 option=GetImageOption(image_info,"quality");
cristy16e74722012-07-01 13:01:38 +00002343 if (option != (const char *) NULL)
2344 {
2345 GeometryInfo
2346 geometry_info;
2347
2348 int
2349 flags;
2350
2351 /*
2352 Set quality scaling for luminance and chrominance separately.
2353 */
2354 flags=ParseGeometry(option,&geometry_info);
2355 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2356 {
2357 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2358 (geometry_info.rho+0.5));
2359 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2360 (geometry_info.sigma+0.5));
2361 jpeg_default_qtables(&jpeg_info,MagickTrue);
2362 }
2363 }
2364#endif
cristy7b8ed292013-05-18 01:13:56 +00002365 colorspace=jpeg_info.in_color_space;
2366 value=GetImageOption(image_info,"jpeg:colorspace");
glennrp2a7dbb12012-09-20 12:48:41 +00002367 if (value == (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002368 value=GetImageProperty(image,"jpeg:colorspace",exception);
cristyefb04e22012-09-17 23:29:25 +00002369 if (value != (char *) NULL)
cristy7b8ed292013-05-18 01:13:56 +00002370 colorspace=StringToInteger(value);
2371 sampling_factor=(const char *) NULL;
2372 if (colorspace == jpeg_info.in_color_space)
cristy3ed852e2009-09-05 21:47:34 +00002373 {
cristy7b8ed292013-05-18 01:13:56 +00002374 value=GetImageOption(image_info,"jpeg:sampling-factor");
2375 if (value == (char *) NULL)
2376 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2377 if (value != (char *) NULL)
2378 {
2379 sampling_factor=value;
2380 if (image->debug != MagickFalse)
2381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2382 " Input sampling-factors=%s",sampling_factor);
2383 }
cristy3ed852e2009-09-05 21:47:34 +00002384 }
cristy7b8ed292013-05-18 01:13:56 +00002385 value=GetImageOption(image_info,"jpeg:sampling-factor");
cristy3ed852e2009-09-05 21:47:34 +00002386 if (image_info->sampling_factor != (char *) NULL)
2387 sampling_factor=image_info->sampling_factor;
2388 if (sampling_factor == (const char *) NULL)
2389 {
2390 if (image->quality >= 90)
2391 for (i=0; i < MAX_COMPONENTS; i++)
2392 {
2393 jpeg_info.comp_info[i].h_samp_factor=1;
2394 jpeg_info.comp_info[i].v_samp_factor=1;
2395 }
2396 }
2397 else
2398 {
2399 char
2400 **factors;
2401
2402 GeometryInfo
2403 geometry_info;
2404
2405 MagickStatusType
2406 flags;
2407
2408 /*
2409 Set sampling factor.
2410 */
2411 i=0;
2412 factors=SamplingFactorToList(sampling_factor);
2413 if (factors != (char **) NULL)
2414 {
2415 for (i=0; i < MAX_COMPONENTS; i++)
2416 {
2417 if (factors[i] == (char *) NULL)
2418 break;
2419 flags=ParseGeometry(factors[i],&geometry_info);
2420 if ((flags & SigmaValue) == 0)
2421 geometry_info.sigma=geometry_info.rho;
2422 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2423 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2424 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2425 }
2426 factors=(char **) RelinquishMagickMemory(factors);
2427 }
2428 for ( ; i < MAX_COMPONENTS; i++)
2429 {
2430 jpeg_info.comp_info[i].h_samp_factor=1;
2431 jpeg_info.comp_info[i].v_samp_factor=1;
2432 }
2433 }
cristy092ec8d2013-04-26 13:46:22 +00002434 option=GetImageOption(image_info,"jpeg:q-table");
cristy1b58f252012-03-01 01:41:41 +00002435 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002436 {
cristy1b58f252012-03-01 01:41:41 +00002437 QuantizationTable
2438 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002439
cristy1445b322012-02-19 18:57:30 +00002440 /*
cristy1b58f252012-03-01 01:41:41 +00002441 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002442 */
cristy1b58f252012-03-01 01:41:41 +00002443 table=GetQuantizationTable(option,"0",exception);
2444 if (table != (QuantizationTable *) NULL)
2445 {
cristyae453b72013-04-21 22:24:20 +00002446 for (i=0; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002447 jpeg_info.comp_info[i].quant_tbl_no=0;
2448 jpeg_add_quant_table(&jpeg_info,0,table->levels,
2449 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002450 table=DestroyQuantizationTable(table);
2451 }
2452 table=GetQuantizationTable(option,"1",exception);
2453 if (table != (QuantizationTable *) NULL)
2454 {
cristyae453b72013-04-21 22:24:20 +00002455 for (i=1; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002456 jpeg_info.comp_info[i].quant_tbl_no=1;
2457 jpeg_add_quant_table(&jpeg_info,1,table->levels,
2458 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002459 table=DestroyQuantizationTable(table);
2460 }
2461 table=GetQuantizationTable(option,"2",exception);
2462 if (table != (QuantizationTable *) NULL)
2463 {
cristyae453b72013-04-21 22:24:20 +00002464 for (i=2; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002465 jpeg_info.comp_info[i].quant_tbl_no=2;
2466 jpeg_add_quant_table(&jpeg_info,2,table->levels,
2467 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002468 table=DestroyQuantizationTable(table);
2469 }
2470 table=GetQuantizationTable(option,"3",exception);
2471 if (table != (QuantizationTable *) NULL)
2472 {
cristyae453b72013-04-21 22:24:20 +00002473 for (i=3; i < MAX_COMPONENTS; i++)
cristyd2fe6d12013-04-21 22:29:56 +00002474 jpeg_info.comp_info[i].quant_tbl_no=3;
2475 jpeg_add_quant_table(&jpeg_info,3,table->levels,
2476 jpeg_quality_scaling(quality),0);
cristy1b58f252012-03-01 01:41:41 +00002477 table=DestroyQuantizationTable(table);
2478 }
cristy1445b322012-02-19 18:57:30 +00002479 }
cristy3ed852e2009-09-05 21:47:34 +00002480 jpeg_start_compress(&jpeg_info,MagickTrue);
2481 if (image->debug != MagickFalse)
2482 {
2483 if (image->storage_class == PseudoClass)
2484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2485 "Storage class: PseudoClass");
2486 else
2487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2488 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2490 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002491 if (image->colors != 0)
2492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002493 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002494 else
2495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2496 "Number of colors: unspecified");
2497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2498 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2499 switch (image->colorspace)
2500 {
2501 case CMYKColorspace:
2502 {
2503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2504 "Storage class: DirectClass");
2505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2506 "Colorspace: CMYK");
2507 break;
2508 }
2509 case YCbCrColorspace:
2510 case Rec601YCbCrColorspace:
2511 case Rec709YCbCrColorspace:
2512 {
2513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2514 "Colorspace: YCbCr");
2515 break;
2516 }
2517 default:
2518 break;
2519 }
2520 switch (image->colorspace)
2521 {
2522 case CMYKColorspace:
2523 {
2524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2525 "Colorspace: CMYK");
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2528 jpeg_info.comp_info[0].h_samp_factor,
2529 jpeg_info.comp_info[0].v_samp_factor,
2530 jpeg_info.comp_info[1].h_samp_factor,
2531 jpeg_info.comp_info[1].v_samp_factor,
2532 jpeg_info.comp_info[2].h_samp_factor,
2533 jpeg_info.comp_info[2].v_samp_factor,
2534 jpeg_info.comp_info[3].h_samp_factor,
2535 jpeg_info.comp_info[3].v_samp_factor);
2536 break;
2537 }
2538 case GRAYColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002539 {
2540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2541 "Colorspace: GRAY");
2542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2543 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2544 jpeg_info.comp_info[0].v_samp_factor);
2545 break;
2546 }
cristyed8d7852013-08-01 22:44:22 +00002547 case sRGBColorspace:
cristy3ed852e2009-09-05 21:47:34 +00002548 case RGBColorspace:
2549 {
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyfeed8be2013-08-01 22:45:13 +00002551 "Image colorspace is RGB");
cristy3ed852e2009-09-05 21:47:34 +00002552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2553 "Sampling factors: %dx%d,%dx%d,%dx%d",
2554 jpeg_info.comp_info[0].h_samp_factor,
2555 jpeg_info.comp_info[0].v_samp_factor,
2556 jpeg_info.comp_info[1].h_samp_factor,
2557 jpeg_info.comp_info[1].v_samp_factor,
2558 jpeg_info.comp_info[2].h_samp_factor,
2559 jpeg_info.comp_info[2].v_samp_factor);
2560 break;
2561 }
2562 case YCbCrColorspace:
2563 case Rec601YCbCrColorspace:
2564 case Rec709YCbCrColorspace:
2565 {
2566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2567 "Colorspace: YCbCr");
2568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2569 "Sampling factors: %dx%d,%dx%d,%dx%d",
2570 jpeg_info.comp_info[0].h_samp_factor,
2571 jpeg_info.comp_info[0].v_samp_factor,
2572 jpeg_info.comp_info[1].h_samp_factor,
2573 jpeg_info.comp_info[1].v_samp_factor,
2574 jpeg_info.comp_info[2].h_samp_factor,
2575 jpeg_info.comp_info[2].v_samp_factor);
2576 break;
2577 }
2578 default:
2579 {
2580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2581 image->colorspace);
2582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2583 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2584 jpeg_info.comp_info[0].h_samp_factor,
2585 jpeg_info.comp_info[0].v_samp_factor,
2586 jpeg_info.comp_info[1].h_samp_factor,
2587 jpeg_info.comp_info[1].v_samp_factor,
2588 jpeg_info.comp_info[2].h_samp_factor,
2589 jpeg_info.comp_info[2].v_samp_factor,
2590 jpeg_info.comp_info[3].h_samp_factor,
2591 jpeg_info.comp_info[3].v_samp_factor);
2592 break;
2593 }
2594 }
2595 }
2596 /*
2597 Write JPEG profiles.
2598 */
cristyd15e6592011-10-15 00:13:06 +00002599 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002600 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002601 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002602 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2603 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2604 if (image->profiles != (void *) NULL)
2605 WriteProfile(&jpeg_info,image);
2606 /*
2607 Convert MIFF to JPEG raster pixels.
2608 */
cristy22646e22013-06-23 16:34:03 +00002609 memory_info=AcquireVirtualMemory((size_t) image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002610 jpeg_info.input_components*sizeof(*jpeg_pixels));
cristy22646e22013-06-23 16:34:03 +00002611 if (memory_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002612 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy22646e22013-06-23 16:34:03 +00002613 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002614 if (setjmp(error_manager.error_recovery) != 0)
2615 {
2616 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002617 if (memory_info != (MemoryInfo *) NULL)
2618 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002619 (void) CloseBlob(image);
2620 return(MagickFalse);
2621 }
2622 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002623 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002624 {
cristy3ed852e2009-09-05 21:47:34 +00002625 if ((jpeg_info.in_color_space == JCS_RGB) ||
2626 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002627 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002628 {
cristy4c08aed2011-07-01 19:47:50 +00002629 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002630 *p;
2631
cristybb503372010-05-27 20:51:26 +00002632 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002633 x;
2634
cristy1e178e72011-08-28 19:44:34 +00002635 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002636 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002637 break;
2638 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002639 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002640 {
cristy4c08aed2011-07-01 19:47:50 +00002641 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2642 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2643 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002644 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002645 }
2646 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002647 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2648 image->rows);
2649 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002650 break;
2651 }
2652 else
cristye90d7402010-03-14 18:21:29 +00002653 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristyd0323222013-04-07 16:13:21 +00002654 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002655 {
cristyd0323222013-04-07 16:13:21 +00002656 register const Quantum
2657 *p;
2658
2659 register ssize_t
2660 x;
2661
2662 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2663 if (p == (const Quantum *) NULL)
2664 break;
2665 q=jpeg_pixels;
2666 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002667 {
cristyd0323222013-04-07 16:13:21 +00002668 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2669 image,p)));
2670 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002671 }
cristyd0323222013-04-07 16:13:21 +00002672 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2673 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2674 image->rows);
2675 if (status == MagickFalse)
2676 break;
2677 }
cristye90d7402010-03-14 18:21:29 +00002678 else
cristybb503372010-05-27 20:51:26 +00002679 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002680 {
cristy4c08aed2011-07-01 19:47:50 +00002681 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002682 *p;
2683
cristybb503372010-05-27 20:51:26 +00002684 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002685 x;
2686
cristy1e178e72011-08-28 19:44:34 +00002687 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002688 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002689 break;
2690 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002691 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002692 {
2693 /*
2694 Convert DirectClass packets to contiguous CMYK scanlines.
2695 */
2696 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002697 GetPixelCyan(image,p))));
cristye90d7402010-03-14 18:21:29 +00002698 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002699 GetPixelMagenta(image,p))));
cristye90d7402010-03-14 18:21:29 +00002700 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy679eaad2013-02-03 22:35:12 +00002701 GetPixelYellow(image,p))));
cristye90d7402010-03-14 18:21:29 +00002702 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002703 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002704 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002705 }
2706 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002707 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2708 image->rows);
2709 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002710 break;
2711 }
2712 }
2713 else
2714 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002715 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002716 {
cristy4c08aed2011-07-01 19:47:50 +00002717 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002718 *p;
2719
cristybb503372010-05-27 20:51:26 +00002720 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002721 x;
2722
cristy1e178e72011-08-28 19:44:34 +00002723 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002724 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002725 break;
2726 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002727 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002728 {
cristyd0323222013-04-07 16:13:21 +00002729 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(
2730 image,p))) >> 4);
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 else
2740 if ((jpeg_info.in_color_space == JCS_RGB) ||
2741 (jpeg_info.in_color_space == JCS_YCbCr))
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 {
cristy4c08aed2011-07-01 19:47:50 +00002756 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2757 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2758 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002759 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002760 }
2761 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002762 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002763 image->rows);
cristye90d7402010-03-14 18:21:29 +00002764 if (status == MagickFalse)
2765 break;
2766 }
2767 else
cristybb503372010-05-27 20:51:26 +00002768 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002769 {
cristy4c08aed2011-07-01 19:47:50 +00002770 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002771 *p;
2772
cristybb503372010-05-27 20:51:26 +00002773 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002774 x;
2775
cristy1e178e72011-08-28 19:44:34 +00002776 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002777 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002778 break;
2779 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002780 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002781 {
2782 /*
2783 Convert DirectClass packets to contiguous CMYK scanlines.
2784 */
cristye90d7402010-03-14 18:21:29 +00002785 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002786 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002787 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002788 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002789 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002790 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002791 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002792 GetPixelBlack(image,p)) >> 4));
cristyed231572011-07-14 02:18:59 +00002793 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002794 }
2795 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002796 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002797 image->rows);
cristye90d7402010-03-14 18:21:29 +00002798 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002799 break;
2800 }
cristybb503372010-05-27 20:51:26 +00002801 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002802 jpeg_finish_compress(&jpeg_info);
2803 /*
2804 Relinquish resources.
2805 */
2806 jpeg_destroy_compress(&jpeg_info);
cristy22646e22013-06-23 16:34:03 +00002807 memory_info=RelinquishVirtualMemory(memory_info);
cristy3ed852e2009-09-05 21:47:34 +00002808 (void) CloseBlob(image);
2809 return(MagickTrue);
2810}
2811#endif