blob: e059453848f9659b57c04ef5711a172c04e3592f [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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*/
42
43/*
44 Include declarations.
45*/
cristy4c08aed2011-07-01 19:47:50 +000046#include "MagickCore/studio.h"
47#include "MagickCore/attribute.h"
48#include "MagickCore/blob.h"
49#include "MagickCore/blob-private.h"
50#include "MagickCore/cache.h"
51#include "MagickCore/color.h"
52#include "MagickCore/colormap-private.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colormap.h"
55#include "MagickCore/colorspace.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/magick.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/module.h"
67#include "MagickCore/monitor.h"
68#include "MagickCore/monitor-private.h"
69#include "MagickCore/option.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/profile.h"
72#include "MagickCore/property.h"
73#include "MagickCore/quantum-private.h"
74#include "MagickCore/resource_.h"
75#include "MagickCore/splay-tree.h"
76#include "MagickCore/static.h"
77#include "MagickCore/string_.h"
78#include "MagickCore/string-private.h"
79#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000080#include <setjmp.h>
81#if defined(MAGICKCORE_JPEG_DELEGATE)
82#define JPEG_INTERNAL_OPTIONS
83#if defined(__MINGW32__)
84# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
cristye7e40552010-04-24 21:34:22 +000085typedef unsigned char boolean;
cristyc6da28e2011-04-28 01:41:35 +000086#define HAVE_BOOLEAN
cristy3ed852e2009-09-05 21:47:34 +000087#endif
88#undef HAVE_STDLIB_H
89#include "jpeglib.h"
90#include "jerror.h"
91#endif
92
93/*
94 Define declarations.
95*/
96#define ICC_MARKER (JPEG_APP0+2)
97#define ICC_PROFILE "ICC_PROFILE"
98#define IPTC_MARKER (JPEG_APP0+13)
99#define XML_MARKER (JPEG_APP0+1)
100#define MaxBufferExtent 8192
101
102/*
103 Typedef declarations.
104*/
105#if defined(MAGICKCORE_JPEG_DELEGATE)
106typedef struct _DestinationManager
107{
108 struct jpeg_destination_mgr
109 manager;
110
111 Image
112 *image;
113
114 JOCTET
115 *buffer;
116} DestinationManager;
117
118typedef struct _ErrorManager
119{
120 Image
121 *image;
122
cristyd28b1dd2011-05-14 20:30:38 +0000123 MagickBooleanType
124 finished;
125
cristy3ed852e2009-09-05 21:47:34 +0000126 jmp_buf
127 error_recovery;
128} ErrorManager;
129
130typedef struct _SourceManager
131{
132 struct jpeg_source_mgr
133 manager;
134
135 Image
136 *image;
137
138 JOCTET
139 *buffer;
140
141 boolean
142 start_of_blob;
143} SourceManager;
144#endif
145
146/*
147 Forward declarations.
148*/
149#if defined(MAGICKCORE_JPEG_DELEGATE)
150static MagickBooleanType
151 WriteJPEGImage(const ImageInfo *,Image *);
152#endif
153
154/*
155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156% %
157% %
158% %
159% I s J P E G %
160% %
161% %
162% %
163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
164%
165% IsJPEG() returns MagickTrue if the image format type, identified by the
166% magick string, is JPEG.
167%
168% The format of the IsJPEG method is:
169%
170% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
171%
172% A description of each parameter follows:
173%
174% o magick: compare image format pattern against these bytes.
175%
176% o length: Specifies the length of the magick string.
177%
178*/
179static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
180{
181 if (length < 3)
182 return(MagickFalse);
183 if (memcmp(magick,"\377\330\377",3) == 0)
184 return(MagickTrue);
185 return(MagickFalse);
186}
187
188#if defined(MAGICKCORE_JPEG_DELEGATE)
189/*
190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191% %
192% %
193% %
194% R e a d J P E G I m a g e %
195% %
196% %
197% %
198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199%
200% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
201% the memory necessary for the new Image structure and returns a pointer to
202% the new image.
203%
204% The format of the ReadJPEGImage method is:
205%
206% Image *ReadJPEGImage(const ImageInfo *image_info,
207% ExceptionInfo *exception)
208%
209% A description of each parameter follows:
210%
211% o image_info: the image info.
212%
213% o exception: return any errors or warnings in this structure.
214%
215*/
216
cristy3ed852e2009-09-05 21:47:34 +0000217static boolean FillInputBuffer(j_decompress_ptr cinfo)
218{
219 SourceManager
220 *source;
221
222 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000223 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
224 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000225 if (source->manager.bytes_in_buffer == 0)
226 {
227 if (source->start_of_blob != 0)
228 ERREXIT(cinfo,JERR_INPUT_EMPTY);
229 WARNMS(cinfo,JWRN_JPEG_EOF);
230 source->buffer[0]=(JOCTET) 0xff;
231 source->buffer[1]=(JOCTET) JPEG_EOI;
232 source->manager.bytes_in_buffer=2;
233 }
234 source->manager.next_input_byte=source->buffer;
235 source->start_of_blob=FALSE;
236 return(TRUE);
237}
238
239static int GetCharacter(j_decompress_ptr jpeg_info)
240{
241 if (jpeg_info->src->bytes_in_buffer == 0)
242 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
243 jpeg_info->src->bytes_in_buffer--;
244 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
245}
246
247static void InitializeSource(j_decompress_ptr cinfo)
248{
249 SourceManager
250 *source;
251
252 source=(SourceManager *) cinfo->src;
253 source->start_of_blob=TRUE;
254}
255
cristye8dd1302009-11-11 02:45:03 +0000256static MagickBooleanType IsITUFaxImage(const Image *image)
257{
258 const StringInfo
259 *profile;
260
261 const unsigned char
262 *datum;
263
cristyace6aa42009-11-11 03:17:33 +0000264 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000265 if (profile == (const StringInfo *) NULL)
266 return(MagickFalse);
267 if (GetStringInfoLength(profile) < 5)
268 return(MagickFalse);
269 datum=GetStringInfoDatum(profile);
270 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
271 (datum[3] == 0x41) && (datum[4] == 0x58))
272 return(MagickTrue);
273 return(MagickFalse);
274}
275
cristyd28b1dd2011-05-14 20:30:38 +0000276static MagickBooleanType JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000277{
cristyd28b1dd2011-05-14 20:30:38 +0000278 char
279 message[JMSG_LENGTH_MAX];
280
cristy3ed852e2009-09-05 21:47:34 +0000281 ErrorManager
282 *error_manager;
283
cristyd28b1dd2011-05-14 20:30:38 +0000284 Image
285 *image;
286
287 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000288 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000289 image=error_manager->image;
cristy86f33542011-05-21 22:58:33 +0000290 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000291 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
293 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000294 if (error_manager->finished != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000295 (void) ThrowMagickException(&image->exception,GetMagickModule(),
296 CorruptImageWarning,(char *) message,image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000297 else
cristy86f33542011-05-21 22:58:33 +0000298 (void) ThrowMagickException(&image->exception,GetMagickModule(),
299 CorruptImageError,(char *) message,image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000300 longjmp(error_manager->error_recovery,1);
301}
302
cristyd28b1dd2011-05-14 20:30:38 +0000303static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
304{
305 char
306 message[JMSG_LENGTH_MAX];
307
308 ErrorManager
309 *error_manager;
310
311 Image
312 *image;
313
314 *message='\0';
315 error_manager=(ErrorManager *) jpeg_info->client_data;
316 image=error_manager->image;
317 if (level < 0)
318 {
319 /*
320 Process warning message.
321 */
322 (jpeg_info->err->format_message)(jpeg_info,message);
323 if ((jpeg_info->err->num_warnings == 0) ||
324 (jpeg_info->err->trace_level >= 3))
325 ThrowBinaryException(CorruptImageWarning,(char *) message,
326 image->filename);
327 jpeg_info->err->num_warnings++;
328 }
329 else
330 if ((image->debug != MagickFalse) &&
331 (level >= jpeg_info->err->trace_level))
332 {
333 /*
334 Process trace message.
335 */
336 (jpeg_info->err->format_message)(jpeg_info,message);
337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
338 "[%s] JPEG Trace: \"%s\"",image->filename,message);
339 }
340 return(MagickTrue);
341}
342
cristy3ed852e2009-09-05 21:47:34 +0000343static boolean ReadComment(j_decompress_ptr jpeg_info)
344{
345 char
346 *comment;
347
348 ErrorManager
349 *error_manager;
350
351 Image
352 *image;
353
354 register char
355 *p;
356
cristybb503372010-05-27 20:51:26 +0000357 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000358 i;
359
360 size_t
361 length;
362
363 /*
364 Determine length of comment.
365 */
366 error_manager=(ErrorManager *) jpeg_info->client_data;
367 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000368 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000369 length+=GetCharacter(jpeg_info);
370 length-=2;
371 if (length <= 0)
372 return(MagickTrue);
373 comment=(char *) NULL;
cristy37e0b382011-06-07 13:31:21 +0000374 if (~length >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000375 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
376 sizeof(*comment));
377 if (comment == (char *) NULL)
378 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
379 image->filename);
380 /*
381 Read comment.
382 */
cristybb503372010-05-27 20:51:26 +0000383 i=(ssize_t) length-1;
cristy3ed852e2009-09-05 21:47:34 +0000384 for (p=comment; i-- >= 0; p++)
385 *p=(char) GetCharacter(jpeg_info);
386 *p='\0';
387 (void) SetImageProperty(image,"comment",comment);
388 comment=DestroyString(comment);
389 return(MagickTrue);
390}
391
392static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
393{
394 char
395 magick[12];
396
397 ErrorManager
398 *error_manager;
399
400 Image
401 *image;
402
403 MagickBooleanType
404 status;
405
cristybb503372010-05-27 20:51:26 +0000406 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000407 i;
408
409 register unsigned char
410 *p;
411
412 size_t
413 length;
414
415 StringInfo
416 *icc_profile,
417 *profile;
418
419 /*
420 Read color profile.
421 */
cristybb503372010-05-27 20:51:26 +0000422 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000423 length+=(size_t) GetCharacter(jpeg_info);
424 length-=2;
425 if (length <= 14)
426 {
427 while (length-- > 0)
428 (void) GetCharacter(jpeg_info);
429 return(MagickTrue);
430 }
431 for (i=0; i < 12; i++)
432 magick[i]=(char) GetCharacter(jpeg_info);
433 if (LocaleCompare(magick,ICC_PROFILE) != 0)
434 {
435 /*
436 Not a ICC profile, return.
437 */
cristybb503372010-05-27 20:51:26 +0000438 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000439 (void) GetCharacter(jpeg_info);
440 return(MagickTrue);
441 }
442 (void) GetCharacter(jpeg_info); /* id */
443 (void) GetCharacter(jpeg_info); /* markers */
444 length-=14;
445 error_manager=(ErrorManager *) jpeg_info->client_data;
446 image=error_manager->image;
447 profile=AcquireStringInfo(length);
448 if (profile == (StringInfo *) NULL)
449 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
450 image->filename);
451 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000452 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000453 *p++=(unsigned char) GetCharacter(jpeg_info);
454 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
455 if (icc_profile != (StringInfo *) NULL)
456 {
457 ConcatenateStringInfo(icc_profile,profile);
458 profile=DestroyStringInfo(profile);
459 }
460 else
461 {
462 status=SetImageProfile(image,"icc",profile);
463 profile=DestroyStringInfo(profile);
464 if (status == MagickFalse)
465 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
466 image->filename);
467 }
468 if (image->debug != MagickFalse)
469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000470 "Profile: ICC, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000471 return(MagickTrue);
472}
473
474static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
475{
476 char
477 magick[MaxTextExtent];
478
479 ErrorManager
480 *error_manager;
481
482 Image
483 *image;
484
485 MagickBooleanType
486 status;
487
cristybb503372010-05-27 20:51:26 +0000488 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000489 i;
490
491 register unsigned char
492 *p;
493
494 size_t
495 length;
496
497 StringInfo
498 *iptc_profile,
499 *profile;
500
501 /*
502 Determine length of binary data stored here.
503 */
cristybb503372010-05-27 20:51:26 +0000504 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000505 length+=(size_t) GetCharacter(jpeg_info);
506 length-=2;
507 if (length <= 14)
508 {
509 while (length-- > 0)
510 (void) GetCharacter(jpeg_info);
511 return(MagickTrue);
512 }
513 /*
514 Validate that this was written as a Photoshop resource format slug.
515 */
516 for (i=0; i < 10; i++)
517 magick[i]=(char) GetCharacter(jpeg_info);
518 magick[10]='\0';
519 if (length <= 10)
520 return(MagickTrue);
521 length-=10;
522 if (LocaleCompare(magick,"Photoshop ") != 0)
523 {
524 /*
525 Not a IPTC profile, return.
526 */
cristybb503372010-05-27 20:51:26 +0000527 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000528 (void) GetCharacter(jpeg_info);
529 return(MagickTrue);
530 }
531 /*
532 Remove the version number.
533 */
534 for (i=0; i < 4; i++)
535 (void) GetCharacter(jpeg_info);
536 if (length <= 4)
537 return(MagickTrue);
538 length-=4;
539 if (length == 0)
540 return(MagickTrue);
541 error_manager=(ErrorManager *) jpeg_info->client_data;
542 image=error_manager->image;
543 profile=AcquireStringInfo(length);
544 if (profile == (StringInfo *) NULL)
545 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
546 image->filename);
547 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000548 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000549 *p++=(unsigned char) GetCharacter(jpeg_info);
550 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
551 if (iptc_profile != (StringInfo *) NULL)
552 {
553 ConcatenateStringInfo(iptc_profile,profile);
554 profile=DestroyStringInfo(profile);
555 }
556 else
557 {
cristy595f1432010-12-11 16:41:53 +0000558 status=SetImageProfile(image,"8bim",profile);
cristy3ed852e2009-09-05 21:47:34 +0000559 profile=DestroyStringInfo(profile);
560 if (status == MagickFalse)
561 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
562 image->filename);
563 }
564 if (image->debug != MagickFalse)
565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000566 "Profile: iptc, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000567 return(MagickTrue);
568}
569
570static boolean ReadProfile(j_decompress_ptr jpeg_info)
571{
572 char
573 name[MaxTextExtent];
574
575 ErrorManager
576 *error_manager;
577
578 Image
579 *image;
580
581 int
582 marker;
583
584 MagickBooleanType
585 status;
586
cristybb503372010-05-27 20:51:26 +0000587 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000588 i;
589
590 register unsigned char
591 *p;
592
593 size_t
594 length;
595
596 StringInfo
597 *profile;
598
599 /*
600 Read generic profile.
601 */
cristybb503372010-05-27 20:51:26 +0000602 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000603 length+=(size_t) GetCharacter(jpeg_info);
604 if (length <= 2)
605 return(MagickTrue);
606 length-=2;
607 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000608 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000609 error_manager=(ErrorManager *) jpeg_info->client_data;
610 image=error_manager->image;
611 profile=AcquireStringInfo(length);
612 if (profile == (StringInfo *) NULL)
613 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
614 image->filename);
615 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000616 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000617 *p++=(unsigned char) GetCharacter(jpeg_info);
618 if (marker == 1)
619 {
620 p=GetStringInfoDatum(profile);
621 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
622 (void) CopyMagickString(name,"exif",MaxTextExtent);
623 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
624 {
cristybb503372010-05-27 20:51:26 +0000625 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000626 j;
627
628 /*
629 Extract namespace from XMP profile.
630 */
631 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000632 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000633 {
634 if (*p == '\0')
635 break;
636 p++;
637 }
cristybb503372010-05-27 20:51:26 +0000638 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000639 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
640 (void) CopyMagickString(name,"xmp",MaxTextExtent);
641 }
642 }
643 status=SetImageProfile(image,name,profile);
644 profile=DestroyStringInfo(profile);
645 if (status == MagickFalse)
646 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
647 image->filename);
648 if (image->debug != MagickFalse)
649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000650 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000651 return(MagickTrue);
652}
653
cristyf2faecf2010-05-28 19:19:36 +0000654static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000655{
656 SourceManager
657 *source;
658
659 if (number_bytes <= 0)
660 return;
661 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000662 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000663 {
cristyf2faecf2010-05-28 19:19:36 +0000664 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000665 (void) FillInputBuffer(cinfo);
666 }
cristy4cb162a2010-05-30 03:04:47 +0000667 source->manager.next_input_byte+=number_bytes;
668 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000669}
670
671static void TerminateSource(j_decompress_ptr cinfo)
672{
673 cinfo=cinfo;
674}
675
676static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
677{
678 SourceManager
679 *source;
680
681 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
682 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
683 source=(SourceManager *) cinfo->src;
684 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
685 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
686 source=(SourceManager *) cinfo->src;
687 source->manager.init_source=InitializeSource;
688 source->manager.fill_input_buffer=FillInputBuffer;
689 source->manager.skip_input_data=SkipInputData;
690 source->manager.resync_to_restart=jpeg_resync_to_restart;
691 source->manager.term_source=TerminateSource;
692 source->manager.bytes_in_buffer=0;
693 source->manager.next_input_byte=NULL;
694 source->image=image;
695}
696
697static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
698 Image *image)
699{
700 image->quality=UndefinedCompressionQuality;
701#if defined(D_PROGRESSIVE_SUPPORTED)
702 if (image->compression == LosslessJPEGCompression)
703 {
704 image->quality=100;
705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
706 "Quality: 100 (lossless)");
707 }
708 else
709#endif
710 {
cristybb503372010-05-27 20:51:26 +0000711 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000712 j,
713 qvalue,
714 sum;
715
cristybb503372010-05-27 20:51:26 +0000716 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000717 i;
718
719 /*
720 Determine the JPEG compression quality from the quantization tables.
721 */
722 sum=0;
723 for (i=0; i < NUM_QUANT_TBLS; i++)
724 {
725 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
726 for (j=0; j < DCTSIZE2; j++)
727 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
728 }
729 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
730 (jpeg_info->quant_tbl_ptrs[1] != NULL))
731 {
cristybb503372010-05-27 20:51:26 +0000732 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000733 hash[101] =
734 {
735 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
736 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
737 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
738 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
739 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
740 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
741 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
742 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
743 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
744 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
745 0
746 },
747 sums[101] =
748 {
749 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
750 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
751 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
752 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
753 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
754 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
755 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
756 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
757 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
758 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
759 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
760 128, 0
761 };
762
cristybb503372010-05-27 20:51:26 +0000763 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000764 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
765 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
766 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
767 for (i=0; i < 100; i++)
768 {
769 if ((qvalue < hash[i]) && (sum < sums[i]))
770 continue;
cristyb6c017e2010-01-13 19:11:39 +0000771 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000772 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000773 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000775 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000776 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000777 break;
778 }
779 }
780 else
781 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
782 {
cristybb503372010-05-27 20:51:26 +0000783 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000784 hash[101] =
785 {
786 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
787 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
788 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
789 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
790 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
791 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
792 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
793 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
794 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
795 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
796 0
797 },
798 sums[101] =
799 {
800 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
801 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
802 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
803 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
804 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
805 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
806 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
807 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
808 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
809 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
810 667, 592, 518, 441, 369, 292, 221, 151, 86,
811 64, 0
812 };
813
cristybb503372010-05-27 20:51:26 +0000814 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000815 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
816 for (i=0; i < 100; i++)
817 {
818 if ((qvalue < hash[i]) && (sum < sums[i]))
819 continue;
cristyb6c017e2010-01-13 19:11:39 +0000820 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000821 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000822 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000824 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000825 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000826 break;
827 }
828 }
829 }
830}
831
832static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image)
833{
834 char
835 sampling_factor[MaxTextExtent];
836
837 switch (jpeg_info->out_color_space)
838 {
839 case JCS_CMYK:
840 {
841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000842 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000843 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
844 jpeg_info->comp_info[0].v_samp_factor,
845 jpeg_info->comp_info[1].h_samp_factor,
846 jpeg_info->comp_info[1].v_samp_factor,
847 jpeg_info->comp_info[2].h_samp_factor,
848 jpeg_info->comp_info[2].v_samp_factor,
849 jpeg_info->comp_info[3].h_samp_factor,
850 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000851 break;
cristy3ed852e2009-09-05 21:47:34 +0000852 }
853 case JCS_GRAYSCALE:
854 {
855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
856 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000857 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000858 jpeg_info->comp_info[0].h_samp_factor,
859 jpeg_info->comp_info[0].v_samp_factor);
860 break;
861 }
862 case JCS_RGB:
863 {
864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000865 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000866 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
867 jpeg_info->comp_info[0].v_samp_factor,
868 jpeg_info->comp_info[1].h_samp_factor,
869 jpeg_info->comp_info[1].v_samp_factor,
870 jpeg_info->comp_info[2].h_samp_factor,
871 jpeg_info->comp_info[2].v_samp_factor);
872 break;
873 }
874 default:
875 {
876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
877 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000878 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000879 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
880 jpeg_info->comp_info[0].v_samp_factor,
881 jpeg_info->comp_info[1].h_samp_factor,
882 jpeg_info->comp_info[1].v_samp_factor,
883 jpeg_info->comp_info[2].h_samp_factor,
884 jpeg_info->comp_info[2].v_samp_factor,
885 jpeg_info->comp_info[3].h_samp_factor,
886 jpeg_info->comp_info[3].v_samp_factor);
887 break;
888 }
889 }
890 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor);
cristye90d7402010-03-14 18:21:29 +0000891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
892 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000893}
894
895static Image *ReadJPEGImage(const ImageInfo *image_info,
896 ExceptionInfo *exception)
897{
898 char
899 value[MaxTextExtent];
900
cristycaec74e2009-09-14 02:20:04 +0000901 const char
cristy11151212009-09-14 13:10:15 +0000902 *option;
cristycaec74e2009-09-14 02:20:04 +0000903
cristy3ed852e2009-09-05 21:47:34 +0000904 ErrorManager
905 error_manager;
906
cristy3ed852e2009-09-05 21:47:34 +0000907 Image
908 *image;
909
cristy3ed852e2009-09-05 21:47:34 +0000910 JSAMPLE
911 *jpeg_pixels;
912
913 JSAMPROW
914 scanline[1];
915
916 MagickBooleanType
917 debug,
918 status;
919
920 MagickSizeType
921 number_pixels;
922
cristy4c08aed2011-07-01 19:47:50 +0000923 Quantum
924 index;
925
cristybb503372010-05-27 20:51:26 +0000926 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000927 i;
928
929 struct jpeg_decompress_struct
930 jpeg_info;
931
932 struct jpeg_error_mgr
933 jpeg_error;
934
935 register JSAMPLE
936 *p;
937
cristybb503372010-05-27 20:51:26 +0000938 size_t
cristy3ed852e2009-09-05 21:47:34 +0000939 precision,
940 units;
941
cristy524222d2011-04-25 00:37:06 +0000942 ssize_t
943 y;
944
cristy3ed852e2009-09-05 21:47:34 +0000945 /*
946 Open image file.
947 */
948 assert(image_info != (const ImageInfo *) NULL);
949 assert(image_info->signature == MagickSignature);
950 if (image_info->debug != MagickFalse)
951 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
952 image_info->filename);
953 assert(exception != (ExceptionInfo *) NULL);
954 assert(exception->signature == MagickSignature);
955 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +0000956 (void) debug;
cristy3ed852e2009-09-05 21:47:34 +0000957 image=AcquireImage(image_info);
958 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
959 if (status == MagickFalse)
960 {
961 image=DestroyImageList(image);
962 return((Image *) NULL);
963 }
964 /*
965 Initialize JPEG parameters.
966 */
cristy91044972011-04-22 14:21:16 +0000967 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +0000968 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
969 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
970 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +0000971 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +0000972 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
973 jpeg_pixels=(JSAMPLE *) NULL;
974 error_manager.image=image;
975 if (setjmp(error_manager.error_recovery) != 0)
976 {
977 jpeg_destroy_decompress(&jpeg_info);
978 (void) CloseBlob(image);
979 number_pixels=(MagickSizeType) image->columns*image->rows;
980 if (number_pixels != 0)
981 return(GetFirstImageInList(image));
cristy195f6d12009-11-05 18:16:03 +0000982 InheritException(exception,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +0000983 return(DestroyImage(image));
984 }
985 jpeg_info.client_data=(void *) &error_manager;
986 jpeg_create_decompress(&jpeg_info);
987 JPEGSourceManager(&jpeg_info,image);
988 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
989 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
990 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
991 for (i=1; i < 16; i++)
992 if ((i != 2) && (i != 13) && (i != 14))
993 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +0000994 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +0000995 if ((image_info->colorspace == YCbCrColorspace) ||
996 (image_info->colorspace == Rec601YCbCrColorspace) ||
997 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +0000998 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +0000999 if (IsITUFaxImage(image) != MagickFalse)
1000 {
1001 image->colorspace=LabColorspace;
1002 jpeg_info.out_color_space=JCS_YCbCr;
1003 }
1004 else
1005 if (jpeg_info.out_color_space == JCS_CMYK)
1006 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001007 /*
1008 Set image resolution.
1009 */
1010 units=0;
1011 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1012 (jpeg_info.Y_density != 1))
1013 {
1014 image->x_resolution=(double) jpeg_info.X_density;
1015 image->y_resolution=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001016 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001017 }
1018 if (units == 1)
1019 image->units=PixelsPerInchResolution;
1020 if (units == 2)
1021 image->units=PixelsPerCentimeterResolution;
1022 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +00001023 option=GetImageOption(image_info,"jpeg:size");
1024 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001025 {
1026 double
1027 scale_factor;
1028
cristycaec74e2009-09-14 02:20:04 +00001029 GeometryInfo
1030 geometry_info;
1031
cristy0adb4f92009-11-28 18:08:51 +00001032 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001033 flags;
1034
cristy3ed852e2009-09-05 21:47:34 +00001035 /*
cristycaec74e2009-09-14 02:20:04 +00001036 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001037 */
cristy11151212009-09-14 13:10:15 +00001038 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001039 if ((flags & SigmaValue) == 0)
1040 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001041 jpeg_calc_output_dimensions(&jpeg_info);
1042 image->magick_columns=jpeg_info.output_width;
1043 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001044 scale_factor=1.0;
1045 if (geometry_info.rho != 0.0)
1046 scale_factor=jpeg_info.output_width/geometry_info.rho;
1047 if ((geometry_info.sigma != 0.0) &&
1048 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1049 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001050 jpeg_info.scale_num=1U;
1051 jpeg_info.scale_denom=(unsigned int) scale_factor;
1052 jpeg_calc_output_dimensions(&jpeg_info);
1053 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1055 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001056 }
cristybb503372010-05-27 20:51:26 +00001057 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001058#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1059#if defined(D_LOSSLESS_SUPPORTED)
1060 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1061 JPEGInterlace : NoInterlace;
1062 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1063 LosslessJPEGCompression : JPEGCompression;
1064 if (jpeg_info.data_precision > 8)
1065 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1066 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1067 image->filename);
1068 if (jpeg_info.data_precision == 16)
1069 jpeg_info.data_precision=12;
1070#else
1071 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1072 NoInterlace;
1073 image->compression=JPEGCompression;
1074#endif
1075#else
1076 image->compression=JPEGCompression;
1077 image->interlace=JPEGInterlace;
1078#endif
1079 if ((image_info->colors > 8) && (image_info->colors <= 256))
1080 {
1081 /*
1082 Let the JPEG library quantize for us.
1083 */
1084 jpeg_info.quantize_colors=MagickTrue;
1085 jpeg_info.desired_number_of_colors=(int) image_info->colors;
1086 }
cristy1b8d4462009-10-27 13:46:56 +00001087 option=GetImageOption(image_info,"jpeg:block-smoothing");
1088 if (option != (const char *) NULL)
1089 {
1090 jpeg_info.do_block_smoothing=MagickFalse;
1091 if (IsMagickTrue(option) != MagickFalse)
1092 jpeg_info.do_block_smoothing=MagickTrue;
1093 }
cristy787d4352010-03-06 13:55:58 +00001094 option=GetImageOption(image_info,"jpeg:dct-method");
1095 if (option != (const char *) NULL)
1096 switch (*option)
1097 {
1098 case 'D':
1099 case 'd':
1100 {
1101 if (LocaleCompare(option,"default") == 0)
1102 jpeg_info.dct_method=JDCT_DEFAULT;
1103 break;
1104 }
1105 case 'F':
1106 case 'f':
1107 {
1108 if (LocaleCompare(option,"fastest") == 0)
1109 jpeg_info.dct_method=JDCT_FASTEST;
1110 if (LocaleCompare(option,"float") == 0)
1111 jpeg_info.dct_method=JDCT_FLOAT;
1112 break;
1113 }
1114 case 'I':
1115 case 'i':
1116 {
1117 if (LocaleCompare(option,"ifast") == 0)
1118 jpeg_info.dct_method=JDCT_IFAST;
1119 if (LocaleCompare(option,"islow") == 0)
1120 jpeg_info.dct_method=JDCT_ISLOW;
1121 break;
1122 }
1123 }
cristy1b8d4462009-10-27 13:46:56 +00001124 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1125 if (option != (const char *) NULL)
1126 {
1127 jpeg_info.do_fancy_upsampling=MagickFalse;
1128 if (IsMagickTrue(option) != MagickFalse)
1129 jpeg_info.do_fancy_upsampling=MagickTrue;
1130 }
cristy3ed852e2009-09-05 21:47:34 +00001131 (void) jpeg_start_decompress(&jpeg_info);
1132 image->columns=jpeg_info.output_width;
1133 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001134 image->depth=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001135 if (jpeg_info.out_color_space == JCS_YCbCr)
1136 image->colorspace=YCbCrColorspace;
1137 if (jpeg_info.out_color_space == JCS_CMYK)
1138 image->colorspace=CMYKColorspace;
1139 if ((image_info->colors != 0) && (image_info->colors <= 256))
1140 if (AcquireImageColormap(image,image_info->colors) == MagickFalse)
1141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1142 if ((jpeg_info.output_components == 1) &&
1143 (jpeg_info.quantize_colors == MagickFalse))
1144 {
cristybb503372010-05-27 20:51:26 +00001145 size_t
cristy3ed852e2009-09-05 21:47:34 +00001146 colors;
1147
cristybb503372010-05-27 20:51:26 +00001148 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy3ed852e2009-09-05 21:47:34 +00001149 if (AcquireImageColormap(image,colors) == MagickFalse)
1150 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1151 }
1152 if (image->debug != MagickFalse)
1153 {
1154 if (image->interlace != NoInterlace)
1155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1156 "Interlace: progressive");
1157 else
1158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1159 "Interlace: nonprogressive");
1160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1161 (int) jpeg_info.data_precision);
1162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1163 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1164 }
1165 JPEGSetImageQuality(&jpeg_info,image);
1166 JPEGSetImageSamplingFactor(&jpeg_info,image);
cristyb51dff52011-05-19 16:55:47 +00001167 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001168 jpeg_info.out_color_space);
1169 (void) SetImageProperty(image,"jpeg:colorspace",value);
1170 if (image_info->ping != MagickFalse)
1171 {
1172 jpeg_destroy_decompress(&jpeg_info);
1173 (void) CloseBlob(image);
1174 return(GetFirstImageInList(image));
1175 }
1176 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1177 jpeg_info.output_components*sizeof(JSAMPLE));
1178 if (jpeg_pixels == (JSAMPLE *) NULL)
1179 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1180 /*
1181 Convert JPEG pixels to pixel packets.
1182 */
1183 if (setjmp(error_manager.error_recovery) != 0)
1184 {
1185 if (jpeg_pixels != (unsigned char *) NULL)
1186 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1187 jpeg_destroy_decompress(&jpeg_info);
1188 (void) CloseBlob(image);
1189 number_pixels=(MagickSizeType) image->columns*image->rows;
1190 if (number_pixels != 0)
1191 return(GetFirstImageInList(image));
1192 return(DestroyImage(image));
1193 }
1194 if (jpeg_info.quantize_colors != MagickFalse)
1195 {
cristybb503372010-05-27 20:51:26 +00001196 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001197 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001198 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001199 {
1200 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1201 image->colormap[i].green=image->colormap[i].red;
1202 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001203 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001204 }
1205 else
cristybb503372010-05-27 20:51:26 +00001206 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001207 {
1208 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1209 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1210 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001211 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001212 }
1213 }
1214 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001215 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001216 {
cristybb503372010-05-27 20:51:26 +00001217 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001218 x;
1219
cristy4c08aed2011-07-01 19:47:50 +00001220 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001221 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001222
1223 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1224 {
1225 (void) ThrowMagickException(exception,GetMagickModule(),
1226 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1227 continue;
1228 }
1229 p=jpeg_pixels;
1230 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001231 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001232 break;
cristy3ed852e2009-09-05 21:47:34 +00001233 if (jpeg_info.data_precision > 8)
1234 {
1235 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001236 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001237 {
cristybb503372010-05-27 20:51:26 +00001238 size_t
cristy3ed852e2009-09-05 21:47:34 +00001239 pixel;
1240
1241 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001242 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001243 else
cristybb503372010-05-27 20:51:26 +00001244 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristya0511732010-02-17 02:38:35 +00001245 index=ConstrainColormapIndex(image,pixel);
cristy4c08aed2011-07-01 19:47:50 +00001246 SetPixelIndex(image,index,q);
1247 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001248 p++;
cristy4c08aed2011-07-01 19:47:50 +00001249 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001250 }
1251 else
1252 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001253 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001254 {
cristy4c08aed2011-07-01 19:47:50 +00001255 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1256 (GETJSAMPLE(*p++) << 4)),q);
1257 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1258 (GETJSAMPLE(*p++) << 4)),q);
1259 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1260 (GETJSAMPLE(*p++) << 4)),q);
1261 SetPixelAlpha(image,OpaqueAlpha,q);
1262 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001263 }
1264 else
cristybb503372010-05-27 20:51:26 +00001265 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001266 {
cristy4c08aed2011-07-01 19:47:50 +00001267 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1268 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1269 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1270 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1271 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1272 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1273 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1274 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1275 SetPixelAlpha(image,OpaqueAlpha,q);
1276 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001277 }
1278 }
1279 else
1280 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001281 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001282 {
cristybb503372010-05-27 20:51:26 +00001283 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p));
cristy4c08aed2011-07-01 19:47:50 +00001284 SetPixelIndex(image,index,q);
1285 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001286 p++;
cristy4c08aed2011-07-01 19:47:50 +00001287 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001288 }
1289 else
1290 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001291 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001292 {
cristy4c08aed2011-07-01 19:47:50 +00001293 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1294 GETJSAMPLE(*p++)),q);
1295 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1296 GETJSAMPLE(*p++)),q);
1297 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1298 GETJSAMPLE(*p++)),q);
1299 SetPixelAlpha(image,OpaqueAlpha,q);
1300 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001301 }
1302 else
cristybb503372010-05-27 20:51:26 +00001303 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001304 {
cristy4c08aed2011-07-01 19:47:50 +00001305 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1306 (unsigned char) GETJSAMPLE(*p++)),q);
1307 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1308 (unsigned char) GETJSAMPLE(*p++)),q);
1309 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1310 (unsigned char) GETJSAMPLE(*p++)),q);
1311 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1312 (unsigned char) GETJSAMPLE(*p++)),q);
1313 SetPixelAlpha(image,OpaqueAlpha,q);
1314 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001315 }
1316 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1317 break;
cristy524222d2011-04-25 00:37:06 +00001318 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1319 image->rows);
1320 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001321 {
1322 jpeg_abort_decompress(&jpeg_info);
1323 break;
1324 }
cristy3ed852e2009-09-05 21:47:34 +00001325 }
cristyd28b1dd2011-05-14 20:30:38 +00001326 if (status != MagickFalse)
1327 {
1328 error_manager.finished=MagickTrue;
1329 if (setjmp(error_manager.error_recovery) == 0)
1330 (void) jpeg_finish_decompress(&jpeg_info);
1331 }
cristy3ed852e2009-09-05 21:47:34 +00001332 /*
1333 Free jpeg resources.
1334 */
cristy3ed852e2009-09-05 21:47:34 +00001335 jpeg_destroy_decompress(&jpeg_info);
1336 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1337 (void) CloseBlob(image);
1338 return(GetFirstImageInList(image));
1339}
1340#endif
1341
1342/*
1343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1344% %
1345% %
1346% %
1347% R e g i s t e r J P E G I m a g e %
1348% %
1349% %
1350% %
1351%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1352%
1353% RegisterJPEGImage() adds properties for the JPEG image format to
1354% the list of supported formats. The properties include the image format
1355% tag, a method to read and/or write the format, whether the format
1356% supports the saving of more than one frame to the same file or blob,
1357% whether the format supports native in-memory I/O, and a brief
1358% description of the format.
1359%
1360% The format of the RegisterJPEGImage method is:
1361%
cristybb503372010-05-27 20:51:26 +00001362% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001363%
1364*/
cristybb503372010-05-27 20:51:26 +00001365ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001366{
1367 char
1368 version[MaxTextExtent];
1369
1370 MagickInfo
1371 *entry;
1372
1373 static const char
cristy7138c592009-09-08 13:58:52 +00001374 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001375
1376 *version='\0';
1377#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001378 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001379#endif
1380 entry=SetMagickInfo("JPEG");
1381 entry->thread_support=NoThreadSupport;
1382#if defined(MAGICKCORE_JPEG_DELEGATE)
1383 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1384 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1385#endif
1386 entry->magick=(IsImageFormatHandler *) IsJPEG;
1387 entry->adjoin=MagickFalse;
1388 entry->description=ConstantString(description);
1389 if (*version != '\0')
1390 entry->version=ConstantString(version);
1391 entry->module=ConstantString("JPEG");
1392 (void) RegisterMagickInfo(entry);
1393 entry=SetMagickInfo("JPG");
1394 entry->thread_support=NoThreadSupport;
1395#if defined(MAGICKCORE_JPEG_DELEGATE)
1396 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1397 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1398#endif
1399 entry->adjoin=MagickFalse;
1400 entry->description=ConstantString(description);
1401 if (*version != '\0')
1402 entry->version=ConstantString(version);
1403 entry->module=ConstantString("JPEG");
1404 (void) RegisterMagickInfo(entry);
1405 entry=SetMagickInfo("PJPEG");
1406 entry->thread_support=NoThreadSupport;
1407#if defined(MAGICKCORE_JPEG_DELEGATE)
1408 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1409 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1410#endif
1411 entry->adjoin=MagickFalse;
1412 entry->description=ConstantString(description);
1413 if (*version != '\0')
1414 entry->version=ConstantString(version);
1415 entry->module=ConstantString("JPEG");
1416 (void) RegisterMagickInfo(entry);
1417 return(MagickImageCoderSignature);
1418}
1419
1420/*
1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422% %
1423% %
1424% %
1425% U n r e g i s t e r J P E G I m a g e %
1426% %
1427% %
1428% %
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430%
1431% UnregisterJPEGImage() removes format registrations made by the
1432% JPEG module from the list of supported formats.
1433%
1434% The format of the UnregisterJPEGImage method is:
1435%
1436% UnregisterJPEGImage(void)
1437%
1438*/
1439ModuleExport void UnregisterJPEGImage(void)
1440{
1441 (void) UnregisterMagickInfo("PJPG");
1442 (void) UnregisterMagickInfo("JPEG");
1443 (void) UnregisterMagickInfo("JPG");
1444}
1445
1446#if defined(MAGICKCORE_JPEG_DELEGATE)
1447/*
1448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449% %
1450% %
1451% %
1452% W r i t e J P E G I m a g e %
1453% %
1454% %
1455% %
1456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457%
1458% WriteJPEGImage() writes a JPEG image file and returns it. It
1459% allocates the memory necessary for the new Image structure and returns a
1460% pointer to the new image.
1461%
1462% The format of the WriteJPEGImage method is:
1463%
cristy91044972011-04-22 14:21:16 +00001464% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1465% Image *image)
cristy3ed852e2009-09-05 21:47:34 +00001466%
1467% A description of each parameter follows:
1468%
1469% o image_info: the image info.
1470%
1471% o jpeg_image: The image.
1472%
1473%
1474*/
1475
1476static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1477{
1478 DestinationManager
1479 *destination;
1480
1481 destination=(DestinationManager *) cinfo->dest;
1482 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1483 MaxBufferExtent,destination->buffer);
1484 if (destination->manager.free_in_buffer != MaxBufferExtent)
1485 ERREXIT(cinfo,JERR_FILE_WRITE);
1486 destination->manager.next_output_byte=destination->buffer;
1487 return(TRUE);
1488}
1489
1490static void InitializeDestination(j_compress_ptr cinfo)
1491{
1492 DestinationManager
1493 *destination;
1494
1495 destination=(DestinationManager *) cinfo->dest;
1496 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1497 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1498 destination->manager.next_output_byte=destination->buffer;
1499 destination->manager.free_in_buffer=MaxBufferExtent;
1500}
1501
1502static inline size_t MagickMin(const size_t x,const size_t y)
1503{
1504 if (x < y)
1505 return(x);
1506 return(y);
1507}
1508
1509static void TerminateDestination(j_compress_ptr cinfo)
1510{
1511 DestinationManager
1512 *destination;
1513
1514 destination=(DestinationManager *) cinfo->dest;
1515 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1516 {
1517 ssize_t
1518 count;
1519
1520 count=WriteBlob(destination->image,MaxBufferExtent-
1521 destination->manager.free_in_buffer,destination->buffer);
1522 if (count != (ssize_t)
1523 (MaxBufferExtent-destination->manager.free_in_buffer))
1524 ERREXIT(cinfo,JERR_FILE_WRITE);
1525 }
1526}
1527
1528static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1529{
1530 const char
1531 *name;
1532
1533 const StringInfo
1534 *profile;
1535
1536 MagickBooleanType
1537 iptc;
1538
cristybb503372010-05-27 20:51:26 +00001539 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001540 i;
1541
1542 size_t
cristy524222d2011-04-25 00:37:06 +00001543 length,
1544 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001545
1546 StringInfo
1547 *custom_profile;
1548
cristy3ed852e2009-09-05 21:47:34 +00001549 /*
1550 Save image profile as a APP marker.
1551 */
1552 iptc=MagickFalse;
1553 custom_profile=AcquireStringInfo(65535L);
1554 ResetImageProfileIterator(image);
1555 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1556 {
cristy109e5572010-09-16 18:38:17 +00001557 register unsigned char
1558 *p;
1559
cristy3ed852e2009-09-05 21:47:34 +00001560 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001561 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001562 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001563 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001564 {
1565 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1566 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1567 (unsigned int) length);
1568 }
1569 if (LocaleCompare(name,"ICC") == 0)
1570 {
1571 register unsigned char
1572 *p;
1573
1574 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001575 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001576 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001577 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001578 {
1579 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001580 p[12]=(unsigned char) ((i/65519L)+1);
1581 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1582 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1583 length);
1584 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1585 custom_profile),(unsigned int) (length+tag_length));
1586 }
1587 }
1588 if (((LocaleCompare(name,"IPTC") == 0) ||
1589 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1590 {
cristybb503372010-05-27 20:51:26 +00001591 size_t
cristy3ed852e2009-09-05 21:47:34 +00001592 roundup;
1593
1594 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001595 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001596 {
1597 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001598 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001599 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1600 {
1601 (void) memcpy(p,"Photoshop 3.0 ",14);
1602 tag_length=14;
1603 }
1604 else
1605 {
1606 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1607 tag_length=26;
1608 p[24]=(unsigned char) (length >> 8);
1609 p[25]=(unsigned char) (length & 0xff);
1610 }
1611 p[13]=0x00;
1612 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001613 if (roundup != 0)
1614 p[length+tag_length]='\0';
1615 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1616 custom_profile),(unsigned int) (length+tag_length+roundup));
1617 }
1618 }
1619 if (LocaleCompare(name,"XMP") == 0)
1620 {
1621 StringInfo
1622 *xmp_profile;
1623
1624 /*
1625 Add namespace to XMP profile.
1626 */
1627 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/");
1628 ConcatenateStringInfo(xmp_profile,profile);
1629 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001630 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001631 {
1632 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1633 jpeg_write_marker(jpeg_info,XML_MARKER,
1634 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1635 }
1636 xmp_profile=DestroyStringInfo(xmp_profile);
1637 }
cristye8c25f92010-06-03 00:53:06 +00001638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1639 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001640 name=GetNextImageProfile(image);
1641 }
1642 custom_profile=DestroyStringInfo(custom_profile);
1643}
1644
1645static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1646{
1647 DestinationManager
1648 *destination;
1649
1650 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1651 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1652 destination=(DestinationManager *) cinfo->dest;
1653 destination->manager.init_destination=InitializeDestination;
1654 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1655 destination->manager.term_destination=TerminateDestination;
1656 destination->image=image;
1657}
1658
1659static char **SamplingFactorToList(const char *text)
1660{
1661 char
1662 **textlist;
1663
1664 register char
1665 *q;
1666
1667 register const char
1668 *p;
1669
cristybb503372010-05-27 20:51:26 +00001670 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001671 i;
1672
cristybb503372010-05-27 20:51:26 +00001673 size_t
cristy3ed852e2009-09-05 21:47:34 +00001674 lines;
1675
1676 if (text == (char *) NULL)
1677 return((char **) NULL);
1678 /*
1679 Convert string to an ASCII list.
1680 */
1681 lines=1;
1682 for (p=text; *p != '\0'; p++)
1683 if (*p == ',')
1684 lines++;
1685 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1686 sizeof(*textlist));
1687 if (textlist == (char **) NULL)
1688 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1689 p=text;
cristybb503372010-05-27 20:51:26 +00001690 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001691 {
1692 for (q=(char *) p; *q != '\0'; q++)
1693 if (*q == ',')
1694 break;
1695 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1696 sizeof(*textlist[i]));
1697 if (textlist[i] == (char *) NULL)
1698 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1699 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1700 if (*q == '\r')
1701 q++;
1702 p=q+1;
1703 }
1704 textlist[i]=(char *) NULL;
1705 return(textlist);
1706}
1707
1708static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1709 Image *image)
1710{
1711 const char
1712 *option,
1713 *sampling_factor,
1714 *value;
1715
1716 ErrorManager
1717 error_manager;
1718
1719 JSAMPLE
1720 *jpeg_pixels;
1721
1722 JSAMPROW
1723 scanline[1];
1724
cristy3ed852e2009-09-05 21:47:34 +00001725 MagickBooleanType
1726 status;
1727
1728 register JSAMPLE
1729 *q;
1730
cristybb503372010-05-27 20:51:26 +00001731 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001732 i;
1733
cristy524222d2011-04-25 00:37:06 +00001734 ssize_t
1735 y;
1736
cristy3ed852e2009-09-05 21:47:34 +00001737 struct jpeg_compress_struct
1738 jpeg_info;
1739
1740 struct jpeg_error_mgr
1741 jpeg_error;
1742
1743 /*
1744 Open image file.
1745 */
1746 assert(image_info != (const ImageInfo *) NULL);
1747 assert(image_info->signature == MagickSignature);
1748 assert(image != (Image *) NULL);
1749 assert(image->signature == MagickSignature);
1750 if (image->debug != MagickFalse)
1751 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1752 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1753 if (status == MagickFalse)
1754 return(status);
1755 /*
1756 Initialize JPEG parameters.
1757 */
cristy91044972011-04-22 14:21:16 +00001758 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001759 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1760 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1761 jpeg_info.client_data=(void *) image;
1762 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001763 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00001764 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy3ed852e2009-09-05 21:47:34 +00001765 error_manager.image=image;
1766 jpeg_pixels=(JSAMPLE *) NULL;
1767 if (setjmp(error_manager.error_recovery) != 0)
1768 {
1769 jpeg_destroy_compress(&jpeg_info);
1770 (void) CloseBlob(image);
1771 return(MagickFalse);
1772 }
1773 jpeg_info.client_data=(void *) &error_manager;
1774 jpeg_create_compress(&jpeg_info);
1775 JPEGDestinationManager(&jpeg_info,image);
1776 if ((image->columns != (unsigned int) image->columns) ||
1777 (image->rows != (unsigned int) image->rows))
1778 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1779 jpeg_info.image_width=(unsigned int) image->columns;
1780 jpeg_info.image_height=(unsigned int) image->rows;
1781 jpeg_info.input_components=3;
1782 jpeg_info.data_precision=8;
1783 jpeg_info.in_color_space=JCS_RGB;
1784 switch (image->colorspace)
1785 {
1786 case CMYKColorspace:
1787 {
1788 jpeg_info.input_components=4;
1789 jpeg_info.in_color_space=JCS_CMYK;
1790 break;
1791 }
1792 case YCbCrColorspace:
1793 case Rec601YCbCrColorspace:
1794 case Rec709YCbCrColorspace:
1795 {
1796 jpeg_info.in_color_space=JCS_YCbCr;
1797 break;
1798 }
1799 case GRAYColorspace:
1800 case Rec601LumaColorspace:
1801 case Rec709LumaColorspace:
1802 {
1803 jpeg_info.input_components=1;
1804 jpeg_info.in_color_space=JCS_GRAYSCALE;
1805 break;
1806 }
1807 default:
cristy9f396782009-12-21 01:37:45 +00001808 {
1809 if (image->colorspace != RGBColorspace)
1810 (void) TransformImageColorspace(image,RGBColorspace);
cristy3ed852e2009-09-05 21:47:34 +00001811 break;
cristy9f396782009-12-21 01:37:45 +00001812 }
cristy3ed852e2009-09-05 21:47:34 +00001813 }
1814 if ((image_info->type != TrueColorType) &&
cristy4c08aed2011-07-01 19:47:50 +00001815 (IsImageGray(image,&image->exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001816 {
1817 jpeg_info.input_components=1;
1818 jpeg_info.in_color_space=JCS_GRAYSCALE;
1819 }
1820 jpeg_set_defaults(&jpeg_info);
1821 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1822 jpeg_info.data_precision=8;
1823 else
1824 if (sizeof(JSAMPLE) > 1)
1825 jpeg_info.data_precision=12;
1826 jpeg_info.density_unit=(UINT8) 1;
1827 if (image->debug != MagickFalse)
1828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001829 "Image resolution: %.20g,%.20g",floor(image->x_resolution+0.5),
1830 floor(image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001831 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1832 {
1833 /*
1834 Set image resolution.
1835 */
1836 jpeg_info.write_JFIF_header=MagickTrue;
cristyd999ef62010-05-08 19:54:26 +00001837 jpeg_info.X_density=(UINT16) floor(image->x_resolution+0.5);
1838 jpeg_info.Y_density=(UINT16) floor(image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001839 if (image->units == PixelsPerInchResolution)
1840 jpeg_info.density_unit=(UINT8) 1;
1841 if (image->units == PixelsPerCentimeterResolution)
1842 jpeg_info.density_unit=(UINT8) 2;
1843 }
1844 option=GetImageOption(image_info,"jpeg:dct-method");
1845 if (option != (const char *) NULL)
1846 switch (*option)
1847 {
1848 case 'D':
1849 case 'd':
1850 {
1851 if (LocaleCompare(option,"default") == 0)
1852 jpeg_info.dct_method=JDCT_DEFAULT;
1853 break;
1854 }
1855 case 'F':
1856 case 'f':
1857 {
1858 if (LocaleCompare(option,"fastest") == 0)
1859 jpeg_info.dct_method=JDCT_FASTEST;
1860 if (LocaleCompare(option,"float") == 0)
1861 jpeg_info.dct_method=JDCT_FLOAT;
1862 break;
1863 }
1864 case 'I':
1865 case 'i':
1866 {
1867 if (LocaleCompare(option,"ifast") == 0)
1868 jpeg_info.dct_method=JDCT_IFAST;
1869 if (LocaleCompare(option,"islow") == 0)
1870 jpeg_info.dct_method=JDCT_ISLOW;
1871 break;
1872 }
1873 }
1874 option=GetImageOption(image_info,"jpeg:optimize-coding");
1875 if (option != (const char *) NULL)
1876 {
1877 jpeg_info.optimize_coding=MagickFalse;
1878 if (IsMagickTrue(option) != MagickFalse)
1879 jpeg_info.optimize_coding=MagickTrue;
1880 }
1881 else
1882 {
1883 MagickSizeType
1884 length;
1885
1886 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1887 image->rows*sizeof(JSAMPLE);
1888 if (length == (MagickSizeType) ((size_t) length))
1889 {
1890 /*
1891 Perform optimization only if available memory resources permit it.
1892 */
1893 status=AcquireMagickResource(MemoryResource,length);
1894 if (status != MagickFalse)
1895 jpeg_info.optimize_coding=MagickTrue;
1896 RelinquishMagickResource(MemoryResource,length);
1897 }
1898 }
1899#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1900 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1901 (image_info->interlace != NoInterlace))
1902 {
1903 if (image->debug != MagickFalse)
1904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1905 "Interlace: progressive");
1906 jpeg_simple_progression(&jpeg_info);
1907 }
1908 else
1909 if (image->debug != MagickFalse)
1910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1911 "Interlace: non-progressive");
1912#else
1913 if (image->debug != MagickFalse)
1914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1915 "Interlace: nonprogressive");
1916#endif
cristy0adb4f92009-11-28 18:08:51 +00001917 option=GetImageOption(image_info,"jpeg:extent");
1918 if (option != (const char *) NULL)
1919 {
1920 Image
1921 *jpeg_image;
1922
1923 ImageInfo
1924 *jpeg_info;
1925
1926 jpeg_info=CloneImageInfo(image_info);
1927 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1928 if (jpeg_image != (Image *) NULL)
1929 {
1930 MagickSizeType
1931 extent;
1932
1933 size_t
cristy87e73ab2010-02-12 01:59:12 +00001934 maximum,
1935 minimum;
cristy0adb4f92009-11-28 18:08:51 +00001936
1937 /*
1938 Search for compression quality that does not exceed image extent.
1939 */
1940 jpeg_info->quality=0;
cristyf2f27272009-12-17 14:48:46 +00001941 extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00001942 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1943 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00001944 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00001945 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00001946 {
cristy87e73ab2010-02-12 01:59:12 +00001947 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy0adb4f92009-11-28 18:08:51 +00001948 status=WriteJPEGImage(jpeg_info,jpeg_image);
cristyc8d69b12010-02-13 19:06:26 +00001949 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00001950 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00001951 else
cristy87e73ab2010-02-12 01:59:12 +00001952 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00001953 }
1954 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00001955 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00001956 jpeg_image=DestroyImage(jpeg_image);
1957 }
1958 jpeg_info=DestroyImageInfo(jpeg_info);
1959 }
cristy3ed852e2009-09-05 21:47:34 +00001960 if ((image_info->compression != LosslessJPEGCompression) &&
1961 (image->quality <= 100))
1962 {
1963 if (image->quality == UndefinedCompressionQuality)
1964 jpeg_set_quality(&jpeg_info,92,MagickTrue);
1965 else
1966 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
1967 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
1969 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00001970 }
1971 else
1972 {
1973#if !defined(C_LOSSLESS_SUPPORTED)
1974 jpeg_set_quality(&jpeg_info,100,MagickTrue);
1975 if (image->debug != MagickFalse)
1976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
1977#else
1978 if (image->quality < 100)
1979 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1980 CoderWarning,"LosslessToLossyJPEGConversion",image->filename);
1981 else
1982 {
1983 int
1984 point_transform,
1985 predictor;
1986
1987 predictor=image->quality/100; /* range 1-7 */
1988 point_transform=image->quality % 20; /* range 0-15 */
1989 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
1990 if (image->debug != MagickFalse)
1991 {
1992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1993 "Compression: lossless");
1994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1995 "Predictor: %d",predictor);
1996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1997 "Point Transform: %d",point_transform);
1998 }
1999 }
2000#endif
2001 }
2002 sampling_factor=(const char *) NULL;
2003 value=GetImageProperty(image,"jpeg:sampling-factor");
2004 if (value != (char *) NULL)
2005 {
2006 sampling_factor=value;
2007 if (image->debug != MagickFalse)
2008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2009 " Input sampling-factors=%s",sampling_factor);
2010 }
2011 if (image_info->sampling_factor != (char *) NULL)
2012 sampling_factor=image_info->sampling_factor;
2013 if (sampling_factor == (const char *) NULL)
2014 {
2015 if (image->quality >= 90)
2016 for (i=0; i < MAX_COMPONENTS; i++)
2017 {
2018 jpeg_info.comp_info[i].h_samp_factor=1;
2019 jpeg_info.comp_info[i].v_samp_factor=1;
2020 }
2021 }
2022 else
2023 {
2024 char
2025 **factors;
2026
2027 GeometryInfo
2028 geometry_info;
2029
2030 MagickStatusType
2031 flags;
2032
2033 /*
2034 Set sampling factor.
2035 */
2036 i=0;
2037 factors=SamplingFactorToList(sampling_factor);
2038 if (factors != (char **) NULL)
2039 {
2040 for (i=0; i < MAX_COMPONENTS; i++)
2041 {
2042 if (factors[i] == (char *) NULL)
2043 break;
2044 flags=ParseGeometry(factors[i],&geometry_info);
2045 if ((flags & SigmaValue) == 0)
2046 geometry_info.sigma=geometry_info.rho;
2047 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2048 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2049 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2050 }
2051 factors=(char **) RelinquishMagickMemory(factors);
2052 }
2053 for ( ; i < MAX_COMPONENTS; i++)
2054 {
2055 jpeg_info.comp_info[i].h_samp_factor=1;
2056 jpeg_info.comp_info[i].v_samp_factor=1;
2057 }
2058 }
2059 if (jpeg_info.input_components == 1)
2060 for (i=0; i < MAX_COMPONENTS; i++)
2061 {
2062 jpeg_info.comp_info[i].h_samp_factor=1;
2063 jpeg_info.comp_info[i].v_samp_factor=1;
2064 }
2065 jpeg_start_compress(&jpeg_info,MagickTrue);
2066 if (image->debug != MagickFalse)
2067 {
2068 if (image->storage_class == PseudoClass)
2069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2070 "Storage class: PseudoClass");
2071 else
2072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2073 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2075 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002076 if (image->colors != 0)
2077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002078 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002079 else
2080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2081 "Number of colors: unspecified");
2082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2083 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2084 switch (image->colorspace)
2085 {
2086 case CMYKColorspace:
2087 {
2088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2089 "Storage class: DirectClass");
2090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2091 "Colorspace: CMYK");
2092 break;
2093 }
2094 case YCbCrColorspace:
2095 case Rec601YCbCrColorspace:
2096 case Rec709YCbCrColorspace:
2097 {
2098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2099 "Colorspace: YCbCr");
2100 break;
2101 }
2102 default:
2103 break;
2104 }
2105 switch (image->colorspace)
2106 {
2107 case CMYKColorspace:
2108 {
2109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2110 "Colorspace: CMYK");
2111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2112 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2113 jpeg_info.comp_info[0].h_samp_factor,
2114 jpeg_info.comp_info[0].v_samp_factor,
2115 jpeg_info.comp_info[1].h_samp_factor,
2116 jpeg_info.comp_info[1].v_samp_factor,
2117 jpeg_info.comp_info[2].h_samp_factor,
2118 jpeg_info.comp_info[2].v_samp_factor,
2119 jpeg_info.comp_info[3].h_samp_factor,
2120 jpeg_info.comp_info[3].v_samp_factor);
2121 break;
2122 }
2123 case GRAYColorspace:
2124 case Rec601LumaColorspace:
2125 case Rec709LumaColorspace:
2126 {
2127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2128 "Colorspace: GRAY");
2129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2130 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2131 jpeg_info.comp_info[0].v_samp_factor);
2132 break;
2133 }
2134 case RGBColorspace:
2135 {
2136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2137 "Image colorspace is RGB");
2138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2139 "Sampling factors: %dx%d,%dx%d,%dx%d",
2140 jpeg_info.comp_info[0].h_samp_factor,
2141 jpeg_info.comp_info[0].v_samp_factor,
2142 jpeg_info.comp_info[1].h_samp_factor,
2143 jpeg_info.comp_info[1].v_samp_factor,
2144 jpeg_info.comp_info[2].h_samp_factor,
2145 jpeg_info.comp_info[2].v_samp_factor);
2146 break;
2147 }
2148 case YCbCrColorspace:
2149 case Rec601YCbCrColorspace:
2150 case Rec709YCbCrColorspace:
2151 {
2152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2153 "Colorspace: YCbCr");
2154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2155 "Sampling factors: %dx%d,%dx%d,%dx%d",
2156 jpeg_info.comp_info[0].h_samp_factor,
2157 jpeg_info.comp_info[0].v_samp_factor,
2158 jpeg_info.comp_info[1].h_samp_factor,
2159 jpeg_info.comp_info[1].v_samp_factor,
2160 jpeg_info.comp_info[2].h_samp_factor,
2161 jpeg_info.comp_info[2].v_samp_factor);
2162 break;
2163 }
2164 default:
2165 {
2166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2167 image->colorspace);
2168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2169 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2170 jpeg_info.comp_info[0].h_samp_factor,
2171 jpeg_info.comp_info[0].v_samp_factor,
2172 jpeg_info.comp_info[1].h_samp_factor,
2173 jpeg_info.comp_info[1].v_samp_factor,
2174 jpeg_info.comp_info[2].h_samp_factor,
2175 jpeg_info.comp_info[2].v_samp_factor,
2176 jpeg_info.comp_info[3].h_samp_factor,
2177 jpeg_info.comp_info[3].v_samp_factor);
2178 break;
2179 }
2180 }
2181 }
2182 /*
2183 Write JPEG profiles.
2184 */
2185 value=GetImageProperty(image,"comment");
2186 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002187 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002188 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2189 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2190 if (image->profiles != (void *) NULL)
2191 WriteProfile(&jpeg_info,image);
2192 /*
2193 Convert MIFF to JPEG raster pixels.
2194 */
2195 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2196 jpeg_info.input_components*sizeof(*jpeg_pixels));
2197 if (jpeg_pixels == (JSAMPLE *) NULL)
2198 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2199 if (setjmp(error_manager.error_recovery) != 0)
2200 {
2201 jpeg_destroy_compress(&jpeg_info);
2202 if (jpeg_pixels != (unsigned char *) NULL)
2203 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2204 (void) CloseBlob(image);
2205 return(MagickFalse);
2206 }
2207 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002208 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002209 {
cristy3ed852e2009-09-05 21:47:34 +00002210 if ((jpeg_info.in_color_space == JCS_RGB) ||
2211 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002212 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002213 {
cristy4c08aed2011-07-01 19:47:50 +00002214 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002215 *p;
2216
cristybb503372010-05-27 20:51:26 +00002217 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002218 x;
2219
2220 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002221 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002222 break;
2223 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002224 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002225 {
cristy4c08aed2011-07-01 19:47:50 +00002226 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2227 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2228 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
2229 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002230 }
2231 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002232 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2233 image->rows);
2234 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002235 break;
2236 }
2237 else
cristye90d7402010-03-14 18:21:29 +00002238 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002239 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002240 {
cristy4c08aed2011-07-01 19:47:50 +00002241 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002242 *p;
2243
cristybb503372010-05-27 20:51:26 +00002244 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002245 x;
2246
2247 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002248 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002249 break;
2250 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002251 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002252 {
cristy4c08aed2011-07-01 19:47:50 +00002253 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p));
2254 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002255 }
2256 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002257 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2258 image->rows);
2259 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002260 break;
2261 }
2262 else
cristybb503372010-05-27 20:51:26 +00002263 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002264 {
cristy4c08aed2011-07-01 19:47:50 +00002265 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002266 *p;
2267
cristybb503372010-05-27 20:51:26 +00002268 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002269 x;
2270
2271 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002272 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002273 break;
2274 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002275 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002276 {
2277 /*
2278 Convert DirectClass packets to contiguous CMYK scanlines.
2279 */
2280 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002281 GetPixelRed(image,p))));
cristye90d7402010-03-14 18:21:29 +00002282 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002283 GetPixelGreen(image,p))));
cristye90d7402010-03-14 18:21:29 +00002284 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002285 GetPixelBlue(image,p))));
cristye90d7402010-03-14 18:21:29 +00002286 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002287 GetPixelBlack(image,p))));
2288 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002289 }
2290 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002291 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2292 image->rows);
2293 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002294 break;
2295 }
2296 }
2297 else
2298 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002299 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002300 {
cristy4c08aed2011-07-01 19:47:50 +00002301 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002302 *p;
2303
cristybb503372010-05-27 20:51:26 +00002304 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002305 x;
2306
2307 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002308 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002309 break;
2310 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002311 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002312 {
cristy4c08aed2011-07-01 19:47:50 +00002313 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >>
cristye90d7402010-03-14 18:21:29 +00002314 4);
cristy4c08aed2011-07-01 19:47:50 +00002315 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002316 }
2317 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002318 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2319 image->rows);
2320 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002321 break;
2322 }
2323 else
2324 if ((jpeg_info.in_color_space == JCS_RGB) ||
2325 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002326 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002327 {
cristy4c08aed2011-07-01 19:47:50 +00002328 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002329 *p;
2330
cristybb503372010-05-27 20:51:26 +00002331 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002332 x;
2333
2334 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002335 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002336 break;
2337 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002338 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002339 {
cristy4c08aed2011-07-01 19:47:50 +00002340 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2341 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2342 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
2343 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002344 }
2345 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002346 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002347 image->rows);
cristye90d7402010-03-14 18:21:29 +00002348 if (status == MagickFalse)
2349 break;
2350 }
2351 else
cristybb503372010-05-27 20:51:26 +00002352 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002353 {
cristy4c08aed2011-07-01 19:47:50 +00002354 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002355 *p;
2356
cristybb503372010-05-27 20:51:26 +00002357 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002358 x;
2359
2360 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002361 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002362 break;
2363 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002364 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002365 {
2366 /*
2367 Convert DirectClass packets to contiguous CMYK scanlines.
2368 */
cristye90d7402010-03-14 18:21:29 +00002369 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002370 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002371 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002372 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002373 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002374 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002375 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002376 GetPixelBlack(image,p)) >> 4));
2377 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002378 }
2379 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002380 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002381 image->rows);
cristye90d7402010-03-14 18:21:29 +00002382 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002383 break;
2384 }
cristybb503372010-05-27 20:51:26 +00002385 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002386 jpeg_finish_compress(&jpeg_info);
2387 /*
2388 Relinquish resources.
2389 */
2390 jpeg_destroy_compress(&jpeg_info);
2391 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2392 (void) CloseBlob(image);
2393 return(MagickTrue);
2394}
2395#endif