blob: 7808d02872b9183acbdd382b496ae80eb4eeea9a [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"
cristy510d06a2011-07-06 23:43:54 +000056#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000057#include "MagickCore/constitute.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/magick.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/module.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/profile.h"
73#include "MagickCore/property.h"
74#include "MagickCore/quantum-private.h"
75#include "MagickCore/resource_.h"
76#include "MagickCore/splay-tree.h"
77#include "MagickCore/static.h"
78#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
80#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000081#include <setjmp.h>
82#if defined(MAGICKCORE_JPEG_DELEGATE)
83#define JPEG_INTERNAL_OPTIONS
84#if defined(__MINGW32__)
85# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
cristye7e40552010-04-24 21:34:22 +000086typedef unsigned char boolean;
cristyc6da28e2011-04-28 01:41:35 +000087#define HAVE_BOOLEAN
cristy3ed852e2009-09-05 21:47:34 +000088#endif
89#undef HAVE_STDLIB_H
90#include "jpeglib.h"
91#include "jerror.h"
92#endif
93
94/*
95 Define declarations.
96*/
97#define ICC_MARKER (JPEG_APP0+2)
98#define ICC_PROFILE "ICC_PROFILE"
99#define IPTC_MARKER (JPEG_APP0+13)
100#define XML_MARKER (JPEG_APP0+1)
101#define MaxBufferExtent 8192
102
103/*
104 Typedef declarations.
105*/
106#if defined(MAGICKCORE_JPEG_DELEGATE)
107typedef struct _DestinationManager
108{
109 struct jpeg_destination_mgr
110 manager;
111
112 Image
113 *image;
114
115 JOCTET
116 *buffer;
117} DestinationManager;
118
119typedef struct _ErrorManager
120{
121 Image
122 *image;
123
cristyd28b1dd2011-05-14 20:30:38 +0000124 MagickBooleanType
125 finished;
126
cristy3ed852e2009-09-05 21:47:34 +0000127 jmp_buf
128 error_recovery;
129} ErrorManager;
130
131typedef struct _SourceManager
132{
133 struct jpeg_source_mgr
134 manager;
135
136 Image
137 *image;
138
139 JOCTET
140 *buffer;
141
142 boolean
143 start_of_blob;
144} SourceManager;
145#endif
146
147/*
148 Forward declarations.
149*/
150#if defined(MAGICKCORE_JPEG_DELEGATE)
151static MagickBooleanType
152 WriteJPEGImage(const ImageInfo *,Image *);
153#endif
154
155/*
156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157% %
158% %
159% %
160% I s J P E G %
161% %
162% %
163% %
164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
165%
166% IsJPEG() returns MagickTrue if the image format type, identified by the
167% magick string, is JPEG.
168%
169% The format of the IsJPEG method is:
170%
171% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
172%
173% A description of each parameter follows:
174%
175% o magick: compare image format pattern against these bytes.
176%
177% o length: Specifies the length of the magick string.
178%
179*/
180static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
181{
182 if (length < 3)
183 return(MagickFalse);
184 if (memcmp(magick,"\377\330\377",3) == 0)
185 return(MagickTrue);
186 return(MagickFalse);
187}
188
189#if defined(MAGICKCORE_JPEG_DELEGATE)
190/*
191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
192% %
193% %
194% %
195% R e a d J P E G I m a g e %
196% %
197% %
198% %
199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
200%
201% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
202% the memory necessary for the new Image structure and returns a pointer to
203% the new image.
204%
205% The format of the ReadJPEGImage method is:
206%
207% Image *ReadJPEGImage(const ImageInfo *image_info,
208% ExceptionInfo *exception)
209%
210% A description of each parameter follows:
211%
212% o image_info: the image info.
213%
214% o exception: return any errors or warnings in this structure.
215%
216*/
217
cristy3ed852e2009-09-05 21:47:34 +0000218static boolean FillInputBuffer(j_decompress_ptr cinfo)
219{
220 SourceManager
221 *source;
222
223 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000224 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
225 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000226 if (source->manager.bytes_in_buffer == 0)
227 {
228 if (source->start_of_blob != 0)
229 ERREXIT(cinfo,JERR_INPUT_EMPTY);
230 WARNMS(cinfo,JWRN_JPEG_EOF);
231 source->buffer[0]=(JOCTET) 0xff;
232 source->buffer[1]=(JOCTET) JPEG_EOI;
233 source->manager.bytes_in_buffer=2;
234 }
235 source->manager.next_input_byte=source->buffer;
236 source->start_of_blob=FALSE;
237 return(TRUE);
238}
239
240static int GetCharacter(j_decompress_ptr jpeg_info)
241{
242 if (jpeg_info->src->bytes_in_buffer == 0)
243 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
244 jpeg_info->src->bytes_in_buffer--;
245 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
246}
247
248static void InitializeSource(j_decompress_ptr cinfo)
249{
250 SourceManager
251 *source;
252
253 source=(SourceManager *) cinfo->src;
254 source->start_of_blob=TRUE;
255}
256
cristye8dd1302009-11-11 02:45:03 +0000257static MagickBooleanType IsITUFaxImage(const Image *image)
258{
259 const StringInfo
260 *profile;
261
262 const unsigned char
263 *datum;
264
cristyace6aa42009-11-11 03:17:33 +0000265 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000266 if (profile == (const StringInfo *) NULL)
267 return(MagickFalse);
268 if (GetStringInfoLength(profile) < 5)
269 return(MagickFalse);
270 datum=GetStringInfoDatum(profile);
271 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
272 (datum[3] == 0x41) && (datum[4] == 0x58))
273 return(MagickTrue);
274 return(MagickFalse);
275}
276
cristyd28b1dd2011-05-14 20:30:38 +0000277static MagickBooleanType JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000278{
cristyd28b1dd2011-05-14 20:30:38 +0000279 char
280 message[JMSG_LENGTH_MAX];
281
cristy3ed852e2009-09-05 21:47:34 +0000282 ErrorManager
283 *error_manager;
284
cristyd28b1dd2011-05-14 20:30:38 +0000285 Image
286 *image;
287
288 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000289 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000290 image=error_manager->image;
cristy86f33542011-05-21 22:58:33 +0000291 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000292 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
294 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000295 if (error_manager->finished != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000296 (void) ThrowMagickException(&image->exception,GetMagickModule(),
297 CorruptImageWarning,(char *) message,image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000298 else
cristy86f33542011-05-21 22:58:33 +0000299 (void) ThrowMagickException(&image->exception,GetMagickModule(),
300 CorruptImageError,(char *) message,image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000301 longjmp(error_manager->error_recovery,1);
302}
303
cristyd28b1dd2011-05-14 20:30:38 +0000304static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
305{
306 char
307 message[JMSG_LENGTH_MAX];
308
309 ErrorManager
310 *error_manager;
311
312 Image
313 *image;
314
315 *message='\0';
316 error_manager=(ErrorManager *) jpeg_info->client_data;
317 image=error_manager->image;
318 if (level < 0)
319 {
320 /*
321 Process warning message.
322 */
323 (jpeg_info->err->format_message)(jpeg_info,message);
324 if ((jpeg_info->err->num_warnings == 0) ||
325 (jpeg_info->err->trace_level >= 3))
326 ThrowBinaryException(CorruptImageWarning,(char *) message,
327 image->filename);
328 jpeg_info->err->num_warnings++;
329 }
330 else
331 if ((image->debug != MagickFalse) &&
332 (level >= jpeg_info->err->trace_level))
333 {
334 /*
335 Process trace message.
336 */
337 (jpeg_info->err->format_message)(jpeg_info,message);
338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
339 "[%s] JPEG Trace: \"%s\"",image->filename,message);
340 }
341 return(MagickTrue);
342}
343
cristy3ed852e2009-09-05 21:47:34 +0000344static boolean ReadComment(j_decompress_ptr jpeg_info)
345{
346 char
347 *comment;
348
349 ErrorManager
350 *error_manager;
351
352 Image
353 *image;
354
355 register char
356 *p;
357
cristybb503372010-05-27 20:51:26 +0000358 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000359 i;
360
361 size_t
362 length;
363
364 /*
365 Determine length of comment.
366 */
367 error_manager=(ErrorManager *) jpeg_info->client_data;
368 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000369 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000370 length+=GetCharacter(jpeg_info);
371 length-=2;
372 if (length <= 0)
373 return(MagickTrue);
374 comment=(char *) NULL;
cristy37e0b382011-06-07 13:31:21 +0000375 if (~length >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000376 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
377 sizeof(*comment));
378 if (comment == (char *) NULL)
379 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
380 image->filename);
381 /*
382 Read comment.
383 */
cristybb503372010-05-27 20:51:26 +0000384 i=(ssize_t) length-1;
cristy3ed852e2009-09-05 21:47:34 +0000385 for (p=comment; i-- >= 0; p++)
386 *p=(char) GetCharacter(jpeg_info);
387 *p='\0';
388 (void) SetImageProperty(image,"comment",comment);
389 comment=DestroyString(comment);
390 return(MagickTrue);
391}
392
393static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
394{
395 char
396 magick[12];
397
398 ErrorManager
399 *error_manager;
400
401 Image
402 *image;
403
404 MagickBooleanType
405 status;
406
cristybb503372010-05-27 20:51:26 +0000407 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000408 i;
409
410 register unsigned char
411 *p;
412
413 size_t
414 length;
415
416 StringInfo
417 *icc_profile,
418 *profile;
419
420 /*
421 Read color profile.
422 */
cristybb503372010-05-27 20:51:26 +0000423 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000424 length+=(size_t) GetCharacter(jpeg_info);
425 length-=2;
426 if (length <= 14)
427 {
428 while (length-- > 0)
429 (void) GetCharacter(jpeg_info);
430 return(MagickTrue);
431 }
432 for (i=0; i < 12; i++)
433 magick[i]=(char) GetCharacter(jpeg_info);
434 if (LocaleCompare(magick,ICC_PROFILE) != 0)
435 {
436 /*
437 Not a ICC profile, return.
438 */
cristybb503372010-05-27 20:51:26 +0000439 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000440 (void) GetCharacter(jpeg_info);
441 return(MagickTrue);
442 }
443 (void) GetCharacter(jpeg_info); /* id */
444 (void) GetCharacter(jpeg_info); /* markers */
445 length-=14;
446 error_manager=(ErrorManager *) jpeg_info->client_data;
447 image=error_manager->image;
448 profile=AcquireStringInfo(length);
449 if (profile == (StringInfo *) NULL)
450 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
451 image->filename);
452 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000453 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000454 *p++=(unsigned char) GetCharacter(jpeg_info);
455 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
456 if (icc_profile != (StringInfo *) NULL)
457 {
458 ConcatenateStringInfo(icc_profile,profile);
459 profile=DestroyStringInfo(profile);
460 }
461 else
462 {
463 status=SetImageProfile(image,"icc",profile);
464 profile=DestroyStringInfo(profile);
465 if (status == MagickFalse)
466 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
467 image->filename);
468 }
469 if (image->debug != MagickFalse)
470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000471 "Profile: ICC, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000472 return(MagickTrue);
473}
474
475static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
476{
477 char
478 magick[MaxTextExtent];
479
480 ErrorManager
481 *error_manager;
482
483 Image
484 *image;
485
486 MagickBooleanType
487 status;
488
cristybb503372010-05-27 20:51:26 +0000489 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000490 i;
491
492 register unsigned char
493 *p;
494
495 size_t
496 length;
497
498 StringInfo
499 *iptc_profile,
500 *profile;
501
502 /*
503 Determine length of binary data stored here.
504 */
cristybb503372010-05-27 20:51:26 +0000505 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000506 length+=(size_t) GetCharacter(jpeg_info);
507 length-=2;
508 if (length <= 14)
509 {
510 while (length-- > 0)
511 (void) GetCharacter(jpeg_info);
512 return(MagickTrue);
513 }
514 /*
515 Validate that this was written as a Photoshop resource format slug.
516 */
517 for (i=0; i < 10; i++)
518 magick[i]=(char) GetCharacter(jpeg_info);
519 magick[10]='\0';
520 if (length <= 10)
521 return(MagickTrue);
522 length-=10;
523 if (LocaleCompare(magick,"Photoshop ") != 0)
524 {
525 /*
526 Not a IPTC profile, return.
527 */
cristybb503372010-05-27 20:51:26 +0000528 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000529 (void) GetCharacter(jpeg_info);
530 return(MagickTrue);
531 }
532 /*
533 Remove the version number.
534 */
535 for (i=0; i < 4; i++)
536 (void) GetCharacter(jpeg_info);
537 if (length <= 4)
538 return(MagickTrue);
539 length-=4;
540 if (length == 0)
541 return(MagickTrue);
542 error_manager=(ErrorManager *) jpeg_info->client_data;
543 image=error_manager->image;
544 profile=AcquireStringInfo(length);
545 if (profile == (StringInfo *) NULL)
546 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
547 image->filename);
548 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000549 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000550 *p++=(unsigned char) GetCharacter(jpeg_info);
551 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
552 if (iptc_profile != (StringInfo *) NULL)
553 {
554 ConcatenateStringInfo(iptc_profile,profile);
555 profile=DestroyStringInfo(profile);
556 }
557 else
558 {
cristy595f1432010-12-11 16:41:53 +0000559 status=SetImageProfile(image,"8bim",profile);
cristy3ed852e2009-09-05 21:47:34 +0000560 profile=DestroyStringInfo(profile);
561 if (status == MagickFalse)
562 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
563 image->filename);
564 }
565 if (image->debug != MagickFalse)
566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000567 "Profile: iptc, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000568 return(MagickTrue);
569}
570
571static boolean ReadProfile(j_decompress_ptr jpeg_info)
572{
573 char
574 name[MaxTextExtent];
575
576 ErrorManager
577 *error_manager;
578
579 Image
580 *image;
581
582 int
583 marker;
584
585 MagickBooleanType
586 status;
587
cristybb503372010-05-27 20:51:26 +0000588 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000589 i;
590
591 register unsigned char
592 *p;
593
594 size_t
595 length;
596
597 StringInfo
598 *profile;
599
600 /*
601 Read generic profile.
602 */
cristybb503372010-05-27 20:51:26 +0000603 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000604 length+=(size_t) GetCharacter(jpeg_info);
605 if (length <= 2)
606 return(MagickTrue);
607 length-=2;
608 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000609 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000610 error_manager=(ErrorManager *) jpeg_info->client_data;
611 image=error_manager->image;
612 profile=AcquireStringInfo(length);
613 if (profile == (StringInfo *) NULL)
614 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
615 image->filename);
616 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000617 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000618 *p++=(unsigned char) GetCharacter(jpeg_info);
619 if (marker == 1)
620 {
621 p=GetStringInfoDatum(profile);
622 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
623 (void) CopyMagickString(name,"exif",MaxTextExtent);
624 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
625 {
cristybb503372010-05-27 20:51:26 +0000626 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000627 j;
628
629 /*
630 Extract namespace from XMP profile.
631 */
632 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000633 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000634 {
635 if (*p == '\0')
636 break;
637 p++;
638 }
cristybb503372010-05-27 20:51:26 +0000639 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000640 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
641 (void) CopyMagickString(name,"xmp",MaxTextExtent);
642 }
643 }
644 status=SetImageProfile(image,name,profile);
645 profile=DestroyStringInfo(profile);
646 if (status == MagickFalse)
647 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
648 image->filename);
649 if (image->debug != MagickFalse)
650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000651 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000652 return(MagickTrue);
653}
654
cristyf2faecf2010-05-28 19:19:36 +0000655static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000656{
657 SourceManager
658 *source;
659
660 if (number_bytes <= 0)
661 return;
662 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000663 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000664 {
cristyf2faecf2010-05-28 19:19:36 +0000665 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000666 (void) FillInputBuffer(cinfo);
667 }
cristy4cb162a2010-05-30 03:04:47 +0000668 source->manager.next_input_byte+=number_bytes;
669 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000670}
671
672static void TerminateSource(j_decompress_ptr cinfo)
673{
674 cinfo=cinfo;
675}
676
677static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
678{
679 SourceManager
680 *source;
681
682 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
683 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
684 source=(SourceManager *) cinfo->src;
685 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
686 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
687 source=(SourceManager *) cinfo->src;
688 source->manager.init_source=InitializeSource;
689 source->manager.fill_input_buffer=FillInputBuffer;
690 source->manager.skip_input_data=SkipInputData;
691 source->manager.resync_to_restart=jpeg_resync_to_restart;
692 source->manager.term_source=TerminateSource;
693 source->manager.bytes_in_buffer=0;
694 source->manager.next_input_byte=NULL;
695 source->image=image;
696}
697
698static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
699 Image *image)
700{
701 image->quality=UndefinedCompressionQuality;
702#if defined(D_PROGRESSIVE_SUPPORTED)
703 if (image->compression == LosslessJPEGCompression)
704 {
705 image->quality=100;
706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
707 "Quality: 100 (lossless)");
708 }
709 else
710#endif
711 {
cristybb503372010-05-27 20:51:26 +0000712 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000713 j,
714 qvalue,
715 sum;
716
cristybb503372010-05-27 20:51:26 +0000717 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000718 i;
719
720 /*
721 Determine the JPEG compression quality from the quantization tables.
722 */
723 sum=0;
724 for (i=0; i < NUM_QUANT_TBLS; i++)
725 {
726 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
727 for (j=0; j < DCTSIZE2; j++)
728 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
729 }
730 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
731 (jpeg_info->quant_tbl_ptrs[1] != NULL))
732 {
cristybb503372010-05-27 20:51:26 +0000733 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000734 hash[101] =
735 {
736 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
737 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
738 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
739 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
740 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
741 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
742 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
743 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
744 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
745 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
746 0
747 },
748 sums[101] =
749 {
750 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
751 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
752 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
753 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
754 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
755 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
756 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
757 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
758 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
759 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
760 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
761 128, 0
762 };
763
cristybb503372010-05-27 20:51:26 +0000764 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000765 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
766 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
767 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
768 for (i=0; i < 100; i++)
769 {
770 if ((qvalue < hash[i]) && (sum < sums[i]))
771 continue;
cristyb6c017e2010-01-13 19:11:39 +0000772 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000773 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000774 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000776 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000777 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000778 break;
779 }
780 }
781 else
782 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
783 {
cristybb503372010-05-27 20:51:26 +0000784 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000785 hash[101] =
786 {
787 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
788 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
789 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
790 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
791 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
792 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
793 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
794 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
795 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
796 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
797 0
798 },
799 sums[101] =
800 {
801 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
802 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
803 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
804 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
805 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
806 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
807 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
808 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
809 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
810 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
811 667, 592, 518, 441, 369, 292, 221, 151, 86,
812 64, 0
813 };
814
cristybb503372010-05-27 20:51:26 +0000815 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000816 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
817 for (i=0; i < 100; i++)
818 {
819 if ((qvalue < hash[i]) && (sum < sums[i]))
820 continue;
cristyb6c017e2010-01-13 19:11:39 +0000821 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000822 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000823 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000825 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000826 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000827 break;
828 }
829 }
830 }
831}
832
833static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image)
834{
835 char
836 sampling_factor[MaxTextExtent];
837
838 switch (jpeg_info->out_color_space)
839 {
840 case JCS_CMYK:
841 {
842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000843 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000844 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
845 jpeg_info->comp_info[0].v_samp_factor,
846 jpeg_info->comp_info[1].h_samp_factor,
847 jpeg_info->comp_info[1].v_samp_factor,
848 jpeg_info->comp_info[2].h_samp_factor,
849 jpeg_info->comp_info[2].v_samp_factor,
850 jpeg_info->comp_info[3].h_samp_factor,
851 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000852 break;
cristy3ed852e2009-09-05 21:47:34 +0000853 }
854 case JCS_GRAYSCALE:
855 {
856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
857 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000858 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000859 jpeg_info->comp_info[0].h_samp_factor,
860 jpeg_info->comp_info[0].v_samp_factor);
861 break;
862 }
863 case JCS_RGB:
864 {
865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000866 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000867 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
868 jpeg_info->comp_info[0].v_samp_factor,
869 jpeg_info->comp_info[1].h_samp_factor,
870 jpeg_info->comp_info[1].v_samp_factor,
871 jpeg_info->comp_info[2].h_samp_factor,
872 jpeg_info->comp_info[2].v_samp_factor);
873 break;
874 }
875 default:
876 {
877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
878 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000879 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000880 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
881 jpeg_info->comp_info[0].v_samp_factor,
882 jpeg_info->comp_info[1].h_samp_factor,
883 jpeg_info->comp_info[1].v_samp_factor,
884 jpeg_info->comp_info[2].h_samp_factor,
885 jpeg_info->comp_info[2].v_samp_factor,
886 jpeg_info->comp_info[3].h_samp_factor,
887 jpeg_info->comp_info[3].v_samp_factor);
888 break;
889 }
890 }
891 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor);
cristye90d7402010-03-14 18:21:29 +0000892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
893 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000894}
895
896static Image *ReadJPEGImage(const ImageInfo *image_info,
897 ExceptionInfo *exception)
898{
899 char
900 value[MaxTextExtent];
901
cristycaec74e2009-09-14 02:20:04 +0000902 const char
cristy11151212009-09-14 13:10:15 +0000903 *option;
cristycaec74e2009-09-14 02:20:04 +0000904
cristy3ed852e2009-09-05 21:47:34 +0000905 ErrorManager
906 error_manager;
907
cristy3ed852e2009-09-05 21:47:34 +0000908 Image
909 *image;
910
cristy3ed852e2009-09-05 21:47:34 +0000911 JSAMPLE
912 *jpeg_pixels;
913
914 JSAMPROW
915 scanline[1];
916
917 MagickBooleanType
918 debug,
919 status;
920
921 MagickSizeType
922 number_pixels;
923
cristy4c08aed2011-07-01 19:47:50 +0000924 Quantum
925 index;
926
cristybb503372010-05-27 20:51:26 +0000927 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000928 i;
929
930 struct jpeg_decompress_struct
931 jpeg_info;
932
933 struct jpeg_error_mgr
934 jpeg_error;
935
936 register JSAMPLE
937 *p;
938
cristybb503372010-05-27 20:51:26 +0000939 size_t
cristy3ed852e2009-09-05 21:47:34 +0000940 precision,
941 units;
942
cristy524222d2011-04-25 00:37:06 +0000943 ssize_t
944 y;
945
cristy3ed852e2009-09-05 21:47:34 +0000946 /*
947 Open image file.
948 */
949 assert(image_info != (const ImageInfo *) NULL);
950 assert(image_info->signature == MagickSignature);
951 if (image_info->debug != MagickFalse)
952 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
953 image_info->filename);
954 assert(exception != (ExceptionInfo *) NULL);
955 assert(exception->signature == MagickSignature);
956 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +0000957 (void) debug;
cristy3ed852e2009-09-05 21:47:34 +0000958 image=AcquireImage(image_info);
959 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
960 if (status == MagickFalse)
961 {
962 image=DestroyImageList(image);
963 return((Image *) NULL);
964 }
965 /*
966 Initialize JPEG parameters.
967 */
cristy91044972011-04-22 14:21:16 +0000968 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +0000969 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
970 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
971 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +0000972 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +0000973 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
974 jpeg_pixels=(JSAMPLE *) NULL;
975 error_manager.image=image;
976 if (setjmp(error_manager.error_recovery) != 0)
977 {
978 jpeg_destroy_decompress(&jpeg_info);
979 (void) CloseBlob(image);
980 number_pixels=(MagickSizeType) image->columns*image->rows;
981 if (number_pixels != 0)
982 return(GetFirstImageInList(image));
cristy195f6d12009-11-05 18:16:03 +0000983 InheritException(exception,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +0000984 return(DestroyImage(image));
985 }
986 jpeg_info.client_data=(void *) &error_manager;
987 jpeg_create_decompress(&jpeg_info);
988 JPEGSourceManager(&jpeg_info,image);
989 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
990 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
991 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
992 for (i=1; i < 16; i++)
993 if ((i != 2) && (i != 13) && (i != 14))
994 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +0000995 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +0000996 if ((image_info->colorspace == YCbCrColorspace) ||
997 (image_info->colorspace == Rec601YCbCrColorspace) ||
998 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +0000999 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +00001000 if (IsITUFaxImage(image) != MagickFalse)
1001 {
1002 image->colorspace=LabColorspace;
1003 jpeg_info.out_color_space=JCS_YCbCr;
1004 }
1005 else
1006 if (jpeg_info.out_color_space == JCS_CMYK)
1007 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001008 /*
1009 Set image resolution.
1010 */
1011 units=0;
1012 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1013 (jpeg_info.Y_density != 1))
1014 {
1015 image->x_resolution=(double) jpeg_info.X_density;
1016 image->y_resolution=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001017 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001018 }
1019 if (units == 1)
1020 image->units=PixelsPerInchResolution;
1021 if (units == 2)
1022 image->units=PixelsPerCentimeterResolution;
1023 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +00001024 option=GetImageOption(image_info,"jpeg:size");
1025 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001026 {
1027 double
1028 scale_factor;
1029
cristycaec74e2009-09-14 02:20:04 +00001030 GeometryInfo
1031 geometry_info;
1032
cristy0adb4f92009-11-28 18:08:51 +00001033 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001034 flags;
1035
cristy3ed852e2009-09-05 21:47:34 +00001036 /*
cristycaec74e2009-09-14 02:20:04 +00001037 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001038 */
cristy11151212009-09-14 13:10:15 +00001039 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001040 if ((flags & SigmaValue) == 0)
1041 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001042 jpeg_calc_output_dimensions(&jpeg_info);
1043 image->magick_columns=jpeg_info.output_width;
1044 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001045 scale_factor=1.0;
1046 if (geometry_info.rho != 0.0)
1047 scale_factor=jpeg_info.output_width/geometry_info.rho;
1048 if ((geometry_info.sigma != 0.0) &&
1049 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1050 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001051 jpeg_info.scale_num=1U;
1052 jpeg_info.scale_denom=(unsigned int) scale_factor;
1053 jpeg_calc_output_dimensions(&jpeg_info);
1054 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1056 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001057 }
cristybb503372010-05-27 20:51:26 +00001058 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001059#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1060#if defined(D_LOSSLESS_SUPPORTED)
1061 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1062 JPEGInterlace : NoInterlace;
1063 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1064 LosslessJPEGCompression : JPEGCompression;
1065 if (jpeg_info.data_precision > 8)
1066 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1067 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1068 image->filename);
1069 if (jpeg_info.data_precision == 16)
1070 jpeg_info.data_precision=12;
1071#else
1072 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1073 NoInterlace;
1074 image->compression=JPEGCompression;
1075#endif
1076#else
1077 image->compression=JPEGCompression;
1078 image->interlace=JPEGInterlace;
1079#endif
1080 if ((image_info->colors > 8) && (image_info->colors <= 256))
1081 {
1082 /*
1083 Let the JPEG library quantize for us.
1084 */
1085 jpeg_info.quantize_colors=MagickTrue;
1086 jpeg_info.desired_number_of_colors=(int) image_info->colors;
1087 }
cristy1b8d4462009-10-27 13:46:56 +00001088 option=GetImageOption(image_info,"jpeg:block-smoothing");
1089 if (option != (const char *) NULL)
1090 {
1091 jpeg_info.do_block_smoothing=MagickFalse;
1092 if (IsMagickTrue(option) != MagickFalse)
1093 jpeg_info.do_block_smoothing=MagickTrue;
1094 }
cristy787d4352010-03-06 13:55:58 +00001095 option=GetImageOption(image_info,"jpeg:dct-method");
1096 if (option != (const char *) NULL)
1097 switch (*option)
1098 {
1099 case 'D':
1100 case 'd':
1101 {
1102 if (LocaleCompare(option,"default") == 0)
1103 jpeg_info.dct_method=JDCT_DEFAULT;
1104 break;
1105 }
1106 case 'F':
1107 case 'f':
1108 {
1109 if (LocaleCompare(option,"fastest") == 0)
1110 jpeg_info.dct_method=JDCT_FASTEST;
1111 if (LocaleCompare(option,"float") == 0)
1112 jpeg_info.dct_method=JDCT_FLOAT;
1113 break;
1114 }
1115 case 'I':
1116 case 'i':
1117 {
1118 if (LocaleCompare(option,"ifast") == 0)
1119 jpeg_info.dct_method=JDCT_IFAST;
1120 if (LocaleCompare(option,"islow") == 0)
1121 jpeg_info.dct_method=JDCT_ISLOW;
1122 break;
1123 }
1124 }
cristy1b8d4462009-10-27 13:46:56 +00001125 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1126 if (option != (const char *) NULL)
1127 {
1128 jpeg_info.do_fancy_upsampling=MagickFalse;
1129 if (IsMagickTrue(option) != MagickFalse)
1130 jpeg_info.do_fancy_upsampling=MagickTrue;
1131 }
cristy3ed852e2009-09-05 21:47:34 +00001132 (void) jpeg_start_decompress(&jpeg_info);
1133 image->columns=jpeg_info.output_width;
1134 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001135 image->depth=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001136 if (jpeg_info.out_color_space == JCS_YCbCr)
1137 image->colorspace=YCbCrColorspace;
1138 if (jpeg_info.out_color_space == JCS_CMYK)
1139 image->colorspace=CMYKColorspace;
1140 if ((image_info->colors != 0) && (image_info->colors <= 256))
1141 if (AcquireImageColormap(image,image_info->colors) == MagickFalse)
1142 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1143 if ((jpeg_info.output_components == 1) &&
1144 (jpeg_info.quantize_colors == MagickFalse))
1145 {
cristybb503372010-05-27 20:51:26 +00001146 size_t
cristy3ed852e2009-09-05 21:47:34 +00001147 colors;
1148
cristybb503372010-05-27 20:51:26 +00001149 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy3ed852e2009-09-05 21:47:34 +00001150 if (AcquireImageColormap(image,colors) == MagickFalse)
1151 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1152 }
1153 if (image->debug != MagickFalse)
1154 {
1155 if (image->interlace != NoInterlace)
1156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1157 "Interlace: progressive");
1158 else
1159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1160 "Interlace: nonprogressive");
1161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1162 (int) jpeg_info.data_precision);
1163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1164 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1165 }
1166 JPEGSetImageQuality(&jpeg_info,image);
1167 JPEGSetImageSamplingFactor(&jpeg_info,image);
cristyb51dff52011-05-19 16:55:47 +00001168 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001169 jpeg_info.out_color_space);
1170 (void) SetImageProperty(image,"jpeg:colorspace",value);
1171 if (image_info->ping != MagickFalse)
1172 {
1173 jpeg_destroy_decompress(&jpeg_info);
1174 (void) CloseBlob(image);
1175 return(GetFirstImageInList(image));
1176 }
1177 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1178 jpeg_info.output_components*sizeof(JSAMPLE));
1179 if (jpeg_pixels == (JSAMPLE *) NULL)
1180 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1181 /*
1182 Convert JPEG pixels to pixel packets.
1183 */
1184 if (setjmp(error_manager.error_recovery) != 0)
1185 {
1186 if (jpeg_pixels != (unsigned char *) NULL)
1187 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1188 jpeg_destroy_decompress(&jpeg_info);
1189 (void) CloseBlob(image);
1190 number_pixels=(MagickSizeType) image->columns*image->rows;
1191 if (number_pixels != 0)
1192 return(GetFirstImageInList(image));
1193 return(DestroyImage(image));
1194 }
1195 if (jpeg_info.quantize_colors != MagickFalse)
1196 {
cristybb503372010-05-27 20:51:26 +00001197 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001198 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001199 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001200 {
1201 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1202 image->colormap[i].green=image->colormap[i].red;
1203 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001204 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001205 }
1206 else
cristybb503372010-05-27 20:51:26 +00001207 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001208 {
1209 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1210 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1211 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001212 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001213 }
1214 }
1215 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001216 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001217 {
cristybb503372010-05-27 20:51:26 +00001218 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001219 x;
1220
cristy4c08aed2011-07-01 19:47:50 +00001221 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001222 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001223
1224 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1225 {
1226 (void) ThrowMagickException(exception,GetMagickModule(),
1227 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1228 continue;
1229 }
1230 p=jpeg_pixels;
1231 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001232 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001233 break;
cristy3ed852e2009-09-05 21:47:34 +00001234 if (jpeg_info.data_precision > 8)
1235 {
1236 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001237 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001238 {
cristybb503372010-05-27 20:51:26 +00001239 size_t
cristy3ed852e2009-09-05 21:47:34 +00001240 pixel;
1241
1242 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001243 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001244 else
cristybb503372010-05-27 20:51:26 +00001245 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristya0511732010-02-17 02:38:35 +00001246 index=ConstrainColormapIndex(image,pixel);
cristy4c08aed2011-07-01 19:47:50 +00001247 SetPixelIndex(image,index,q);
1248 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001249 p++;
cristyed231572011-07-14 02:18:59 +00001250 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001251 }
1252 else
1253 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001254 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001255 {
cristy4c08aed2011-07-01 19:47:50 +00001256 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1257 (GETJSAMPLE(*p++) << 4)),q);
1258 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1259 (GETJSAMPLE(*p++) << 4)),q);
1260 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1261 (GETJSAMPLE(*p++) << 4)),q);
1262 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001263 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001264 }
1265 else
cristybb503372010-05-27 20:51:26 +00001266 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001267 {
cristy4c08aed2011-07-01 19:47:50 +00001268 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1269 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1270 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1271 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1272 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1273 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1274 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1275 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1276 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001277 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001278 }
1279 }
1280 else
1281 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001282 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001283 {
cristybb503372010-05-27 20:51:26 +00001284 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p));
cristy4c08aed2011-07-01 19:47:50 +00001285 SetPixelIndex(image,index,q);
1286 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001287 p++;
cristyed231572011-07-14 02:18:59 +00001288 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001289 }
1290 else
1291 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001292 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001293 {
cristy4c08aed2011-07-01 19:47:50 +00001294 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1295 GETJSAMPLE(*p++)),q);
1296 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1297 GETJSAMPLE(*p++)),q);
1298 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1299 GETJSAMPLE(*p++)),q);
1300 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001301 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001302 }
1303 else
cristybb503372010-05-27 20:51:26 +00001304 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001305 {
cristy4c08aed2011-07-01 19:47:50 +00001306 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1307 (unsigned char) GETJSAMPLE(*p++)),q);
1308 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1309 (unsigned char) GETJSAMPLE(*p++)),q);
1310 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1311 (unsigned char) GETJSAMPLE(*p++)),q);
1312 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1313 (unsigned char) GETJSAMPLE(*p++)),q);
1314 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001315 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001316 }
1317 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1318 break;
cristy524222d2011-04-25 00:37:06 +00001319 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1320 image->rows);
1321 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001322 {
1323 jpeg_abort_decompress(&jpeg_info);
1324 break;
1325 }
cristy3ed852e2009-09-05 21:47:34 +00001326 }
cristyd28b1dd2011-05-14 20:30:38 +00001327 if (status != MagickFalse)
1328 {
1329 error_manager.finished=MagickTrue;
1330 if (setjmp(error_manager.error_recovery) == 0)
1331 (void) jpeg_finish_decompress(&jpeg_info);
1332 }
cristy3ed852e2009-09-05 21:47:34 +00001333 /*
1334 Free jpeg resources.
1335 */
cristy3ed852e2009-09-05 21:47:34 +00001336 jpeg_destroy_decompress(&jpeg_info);
1337 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1338 (void) CloseBlob(image);
1339 return(GetFirstImageInList(image));
1340}
1341#endif
1342
1343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e g i s t e r J P E G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% RegisterJPEGImage() adds properties for the JPEG image format to
1355% the list of supported formats. The properties include the image format
1356% tag, a method to read and/or write the format, whether the format
1357% supports the saving of more than one frame to the same file or blob,
1358% whether the format supports native in-memory I/O, and a brief
1359% description of the format.
1360%
1361% The format of the RegisterJPEGImage method is:
1362%
cristybb503372010-05-27 20:51:26 +00001363% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001364%
1365*/
cristybb503372010-05-27 20:51:26 +00001366ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001367{
1368 char
1369 version[MaxTextExtent];
1370
1371 MagickInfo
1372 *entry;
1373
1374 static const char
cristy7138c592009-09-08 13:58:52 +00001375 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001376
1377 *version='\0';
1378#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001379 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001380#endif
1381 entry=SetMagickInfo("JPEG");
1382 entry->thread_support=NoThreadSupport;
1383#if defined(MAGICKCORE_JPEG_DELEGATE)
1384 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1385 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1386#endif
1387 entry->magick=(IsImageFormatHandler *) IsJPEG;
1388 entry->adjoin=MagickFalse;
1389 entry->description=ConstantString(description);
1390 if (*version != '\0')
1391 entry->version=ConstantString(version);
1392 entry->module=ConstantString("JPEG");
1393 (void) RegisterMagickInfo(entry);
1394 entry=SetMagickInfo("JPG");
1395 entry->thread_support=NoThreadSupport;
1396#if defined(MAGICKCORE_JPEG_DELEGATE)
1397 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1398 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1399#endif
1400 entry->adjoin=MagickFalse;
1401 entry->description=ConstantString(description);
1402 if (*version != '\0')
1403 entry->version=ConstantString(version);
1404 entry->module=ConstantString("JPEG");
1405 (void) RegisterMagickInfo(entry);
1406 entry=SetMagickInfo("PJPEG");
1407 entry->thread_support=NoThreadSupport;
1408#if defined(MAGICKCORE_JPEG_DELEGATE)
1409 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1410 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1411#endif
1412 entry->adjoin=MagickFalse;
1413 entry->description=ConstantString(description);
1414 if (*version != '\0')
1415 entry->version=ConstantString(version);
1416 entry->module=ConstantString("JPEG");
1417 (void) RegisterMagickInfo(entry);
1418 return(MagickImageCoderSignature);
1419}
1420
1421/*
1422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1423% %
1424% %
1425% %
1426% U n r e g i s t e r J P E G I m a g e %
1427% %
1428% %
1429% %
1430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431%
1432% UnregisterJPEGImage() removes format registrations made by the
1433% JPEG module from the list of supported formats.
1434%
1435% The format of the UnregisterJPEGImage method is:
1436%
1437% UnregisterJPEGImage(void)
1438%
1439*/
1440ModuleExport void UnregisterJPEGImage(void)
1441{
1442 (void) UnregisterMagickInfo("PJPG");
1443 (void) UnregisterMagickInfo("JPEG");
1444 (void) UnregisterMagickInfo("JPG");
1445}
1446
1447#if defined(MAGICKCORE_JPEG_DELEGATE)
1448/*
1449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1450% %
1451% %
1452% %
1453% W r i t e J P E G I m a g e %
1454% %
1455% %
1456% %
1457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458%
1459% WriteJPEGImage() writes a JPEG image file and returns it. It
1460% allocates the memory necessary for the new Image structure and returns a
1461% pointer to the new image.
1462%
1463% The format of the WriteJPEGImage method is:
1464%
cristy91044972011-04-22 14:21:16 +00001465% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1466% Image *image)
cristy3ed852e2009-09-05 21:47:34 +00001467%
1468% A description of each parameter follows:
1469%
1470% o image_info: the image info.
1471%
1472% o jpeg_image: The image.
1473%
1474%
1475*/
1476
1477static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1478{
1479 DestinationManager
1480 *destination;
1481
1482 destination=(DestinationManager *) cinfo->dest;
1483 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1484 MaxBufferExtent,destination->buffer);
1485 if (destination->manager.free_in_buffer != MaxBufferExtent)
1486 ERREXIT(cinfo,JERR_FILE_WRITE);
1487 destination->manager.next_output_byte=destination->buffer;
1488 return(TRUE);
1489}
1490
1491static void InitializeDestination(j_compress_ptr cinfo)
1492{
1493 DestinationManager
1494 *destination;
1495
1496 destination=(DestinationManager *) cinfo->dest;
1497 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1498 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1499 destination->manager.next_output_byte=destination->buffer;
1500 destination->manager.free_in_buffer=MaxBufferExtent;
1501}
1502
1503static inline size_t MagickMin(const size_t x,const size_t y)
1504{
1505 if (x < y)
1506 return(x);
1507 return(y);
1508}
1509
1510static void TerminateDestination(j_compress_ptr cinfo)
1511{
1512 DestinationManager
1513 *destination;
1514
1515 destination=(DestinationManager *) cinfo->dest;
1516 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1517 {
1518 ssize_t
1519 count;
1520
1521 count=WriteBlob(destination->image,MaxBufferExtent-
1522 destination->manager.free_in_buffer,destination->buffer);
1523 if (count != (ssize_t)
1524 (MaxBufferExtent-destination->manager.free_in_buffer))
1525 ERREXIT(cinfo,JERR_FILE_WRITE);
1526 }
1527}
1528
1529static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1530{
1531 const char
1532 *name;
1533
1534 const StringInfo
1535 *profile;
1536
1537 MagickBooleanType
1538 iptc;
1539
cristybb503372010-05-27 20:51:26 +00001540 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001541 i;
1542
1543 size_t
cristy524222d2011-04-25 00:37:06 +00001544 length,
1545 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001546
1547 StringInfo
1548 *custom_profile;
1549
cristy3ed852e2009-09-05 21:47:34 +00001550 /*
1551 Save image profile as a APP marker.
1552 */
1553 iptc=MagickFalse;
1554 custom_profile=AcquireStringInfo(65535L);
1555 ResetImageProfileIterator(image);
1556 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1557 {
cristy109e5572010-09-16 18:38:17 +00001558 register unsigned char
1559 *p;
1560
cristy3ed852e2009-09-05 21:47:34 +00001561 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001562 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001563 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001564 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001565 {
1566 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1567 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1568 (unsigned int) length);
1569 }
1570 if (LocaleCompare(name,"ICC") == 0)
1571 {
1572 register unsigned char
1573 *p;
1574
1575 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001576 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001577 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001578 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001579 {
1580 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001581 p[12]=(unsigned char) ((i/65519L)+1);
1582 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1583 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1584 length);
1585 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1586 custom_profile),(unsigned int) (length+tag_length));
1587 }
1588 }
1589 if (((LocaleCompare(name,"IPTC") == 0) ||
1590 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1591 {
cristybb503372010-05-27 20:51:26 +00001592 size_t
cristy3ed852e2009-09-05 21:47:34 +00001593 roundup;
1594
1595 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001596 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001597 {
1598 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001599 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001600 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1601 {
1602 (void) memcpy(p,"Photoshop 3.0 ",14);
1603 tag_length=14;
1604 }
1605 else
1606 {
1607 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1608 tag_length=26;
1609 p[24]=(unsigned char) (length >> 8);
1610 p[25]=(unsigned char) (length & 0xff);
1611 }
1612 p[13]=0x00;
1613 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001614 if (roundup != 0)
1615 p[length+tag_length]='\0';
1616 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1617 custom_profile),(unsigned int) (length+tag_length+roundup));
1618 }
1619 }
1620 if (LocaleCompare(name,"XMP") == 0)
1621 {
1622 StringInfo
1623 *xmp_profile;
1624
1625 /*
1626 Add namespace to XMP profile.
1627 */
1628 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/");
1629 ConcatenateStringInfo(xmp_profile,profile);
1630 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001631 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001632 {
1633 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1634 jpeg_write_marker(jpeg_info,XML_MARKER,
1635 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1636 }
1637 xmp_profile=DestroyStringInfo(xmp_profile);
1638 }
cristye8c25f92010-06-03 00:53:06 +00001639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1640 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001641 name=GetNextImageProfile(image);
1642 }
1643 custom_profile=DestroyStringInfo(custom_profile);
1644}
1645
1646static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1647{
1648 DestinationManager
1649 *destination;
1650
1651 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1652 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1653 destination=(DestinationManager *) cinfo->dest;
1654 destination->manager.init_destination=InitializeDestination;
1655 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1656 destination->manager.term_destination=TerminateDestination;
1657 destination->image=image;
1658}
1659
1660static char **SamplingFactorToList(const char *text)
1661{
1662 char
1663 **textlist;
1664
1665 register char
1666 *q;
1667
1668 register const char
1669 *p;
1670
cristybb503372010-05-27 20:51:26 +00001671 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001672 i;
1673
cristybb503372010-05-27 20:51:26 +00001674 size_t
cristy3ed852e2009-09-05 21:47:34 +00001675 lines;
1676
1677 if (text == (char *) NULL)
1678 return((char **) NULL);
1679 /*
1680 Convert string to an ASCII list.
1681 */
1682 lines=1;
1683 for (p=text; *p != '\0'; p++)
1684 if (*p == ',')
1685 lines++;
1686 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1687 sizeof(*textlist));
1688 if (textlist == (char **) NULL)
1689 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1690 p=text;
cristybb503372010-05-27 20:51:26 +00001691 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001692 {
1693 for (q=(char *) p; *q != '\0'; q++)
1694 if (*q == ',')
1695 break;
1696 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1697 sizeof(*textlist[i]));
1698 if (textlist[i] == (char *) NULL)
1699 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1700 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1701 if (*q == '\r')
1702 q++;
1703 p=q+1;
1704 }
1705 textlist[i]=(char *) NULL;
1706 return(textlist);
1707}
1708
1709static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1710 Image *image)
1711{
1712 const char
1713 *option,
1714 *sampling_factor,
1715 *value;
1716
1717 ErrorManager
1718 error_manager;
1719
1720 JSAMPLE
1721 *jpeg_pixels;
1722
1723 JSAMPROW
1724 scanline[1];
1725
cristy3ed852e2009-09-05 21:47:34 +00001726 MagickBooleanType
1727 status;
1728
1729 register JSAMPLE
1730 *q;
1731
cristybb503372010-05-27 20:51:26 +00001732 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001733 i;
1734
cristy524222d2011-04-25 00:37:06 +00001735 ssize_t
1736 y;
1737
cristy3ed852e2009-09-05 21:47:34 +00001738 struct jpeg_compress_struct
1739 jpeg_info;
1740
1741 struct jpeg_error_mgr
1742 jpeg_error;
1743
1744 /*
1745 Open image file.
1746 */
1747 assert(image_info != (const ImageInfo *) NULL);
1748 assert(image_info->signature == MagickSignature);
1749 assert(image != (Image *) NULL);
1750 assert(image->signature == MagickSignature);
1751 if (image->debug != MagickFalse)
1752 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1753 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1754 if (status == MagickFalse)
1755 return(status);
1756 /*
1757 Initialize JPEG parameters.
1758 */
cristy91044972011-04-22 14:21:16 +00001759 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001760 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1761 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1762 jpeg_info.client_data=(void *) image;
1763 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001764 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00001765 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy3ed852e2009-09-05 21:47:34 +00001766 error_manager.image=image;
1767 jpeg_pixels=(JSAMPLE *) NULL;
1768 if (setjmp(error_manager.error_recovery) != 0)
1769 {
1770 jpeg_destroy_compress(&jpeg_info);
1771 (void) CloseBlob(image);
1772 return(MagickFalse);
1773 }
1774 jpeg_info.client_data=(void *) &error_manager;
1775 jpeg_create_compress(&jpeg_info);
1776 JPEGDestinationManager(&jpeg_info,image);
1777 if ((image->columns != (unsigned int) image->columns) ||
1778 (image->rows != (unsigned int) image->rows))
1779 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1780 jpeg_info.image_width=(unsigned int) image->columns;
1781 jpeg_info.image_height=(unsigned int) image->rows;
1782 jpeg_info.input_components=3;
1783 jpeg_info.data_precision=8;
1784 jpeg_info.in_color_space=JCS_RGB;
1785 switch (image->colorspace)
1786 {
1787 case CMYKColorspace:
1788 {
1789 jpeg_info.input_components=4;
1790 jpeg_info.in_color_space=JCS_CMYK;
1791 break;
1792 }
1793 case YCbCrColorspace:
1794 case Rec601YCbCrColorspace:
1795 case Rec709YCbCrColorspace:
1796 {
1797 jpeg_info.in_color_space=JCS_YCbCr;
1798 break;
1799 }
1800 case GRAYColorspace:
1801 case Rec601LumaColorspace:
1802 case Rec709LumaColorspace:
1803 {
1804 jpeg_info.input_components=1;
1805 jpeg_info.in_color_space=JCS_GRAYSCALE;
1806 break;
1807 }
1808 default:
cristy9f396782009-12-21 01:37:45 +00001809 {
cristy510d06a2011-07-06 23:43:54 +00001810 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy9f396782009-12-21 01:37:45 +00001811 (void) TransformImageColorspace(image,RGBColorspace);
cristy3ed852e2009-09-05 21:47:34 +00001812 break;
cristy9f396782009-12-21 01:37:45 +00001813 }
cristy3ed852e2009-09-05 21:47:34 +00001814 }
1815 if ((image_info->type != TrueColorType) &&
cristy4c08aed2011-07-01 19:47:50 +00001816 (IsImageGray(image,&image->exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001817 {
1818 jpeg_info.input_components=1;
1819 jpeg_info.in_color_space=JCS_GRAYSCALE;
1820 }
1821 jpeg_set_defaults(&jpeg_info);
1822 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1823 jpeg_info.data_precision=8;
1824 else
1825 if (sizeof(JSAMPLE) > 1)
1826 jpeg_info.data_precision=12;
1827 jpeg_info.density_unit=(UINT8) 1;
1828 if (image->debug != MagickFalse)
1829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001830 "Image resolution: %.20g,%.20g",floor(image->x_resolution+0.5),
1831 floor(image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001832 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1833 {
1834 /*
1835 Set image resolution.
1836 */
1837 jpeg_info.write_JFIF_header=MagickTrue;
cristyd999ef62010-05-08 19:54:26 +00001838 jpeg_info.X_density=(UINT16) floor(image->x_resolution+0.5);
1839 jpeg_info.Y_density=(UINT16) floor(image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001840 if (image->units == PixelsPerInchResolution)
1841 jpeg_info.density_unit=(UINT8) 1;
1842 if (image->units == PixelsPerCentimeterResolution)
1843 jpeg_info.density_unit=(UINT8) 2;
1844 }
1845 option=GetImageOption(image_info,"jpeg:dct-method");
1846 if (option != (const char *) NULL)
1847 switch (*option)
1848 {
1849 case 'D':
1850 case 'd':
1851 {
1852 if (LocaleCompare(option,"default") == 0)
1853 jpeg_info.dct_method=JDCT_DEFAULT;
1854 break;
1855 }
1856 case 'F':
1857 case 'f':
1858 {
1859 if (LocaleCompare(option,"fastest") == 0)
1860 jpeg_info.dct_method=JDCT_FASTEST;
1861 if (LocaleCompare(option,"float") == 0)
1862 jpeg_info.dct_method=JDCT_FLOAT;
1863 break;
1864 }
1865 case 'I':
1866 case 'i':
1867 {
1868 if (LocaleCompare(option,"ifast") == 0)
1869 jpeg_info.dct_method=JDCT_IFAST;
1870 if (LocaleCompare(option,"islow") == 0)
1871 jpeg_info.dct_method=JDCT_ISLOW;
1872 break;
1873 }
1874 }
1875 option=GetImageOption(image_info,"jpeg:optimize-coding");
1876 if (option != (const char *) NULL)
1877 {
1878 jpeg_info.optimize_coding=MagickFalse;
1879 if (IsMagickTrue(option) != MagickFalse)
1880 jpeg_info.optimize_coding=MagickTrue;
1881 }
1882 else
1883 {
1884 MagickSizeType
1885 length;
1886
1887 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1888 image->rows*sizeof(JSAMPLE);
1889 if (length == (MagickSizeType) ((size_t) length))
1890 {
1891 /*
1892 Perform optimization only if available memory resources permit it.
1893 */
1894 status=AcquireMagickResource(MemoryResource,length);
1895 if (status != MagickFalse)
1896 jpeg_info.optimize_coding=MagickTrue;
1897 RelinquishMagickResource(MemoryResource,length);
1898 }
1899 }
1900#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1901 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1902 (image_info->interlace != NoInterlace))
1903 {
1904 if (image->debug != MagickFalse)
1905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1906 "Interlace: progressive");
1907 jpeg_simple_progression(&jpeg_info);
1908 }
1909 else
1910 if (image->debug != MagickFalse)
1911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1912 "Interlace: non-progressive");
1913#else
1914 if (image->debug != MagickFalse)
1915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1916 "Interlace: nonprogressive");
1917#endif
cristy0adb4f92009-11-28 18:08:51 +00001918 option=GetImageOption(image_info,"jpeg:extent");
1919 if (option != (const char *) NULL)
1920 {
1921 Image
1922 *jpeg_image;
1923
1924 ImageInfo
1925 *jpeg_info;
1926
1927 jpeg_info=CloneImageInfo(image_info);
1928 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1929 if (jpeg_image != (Image *) NULL)
1930 {
1931 MagickSizeType
1932 extent;
1933
1934 size_t
cristy87e73ab2010-02-12 01:59:12 +00001935 maximum,
1936 minimum;
cristy0adb4f92009-11-28 18:08:51 +00001937
1938 /*
1939 Search for compression quality that does not exceed image extent.
1940 */
1941 jpeg_info->quality=0;
cristyf2f27272009-12-17 14:48:46 +00001942 extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00001943 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1944 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00001945 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00001946 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00001947 {
cristy87e73ab2010-02-12 01:59:12 +00001948 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy0adb4f92009-11-28 18:08:51 +00001949 status=WriteJPEGImage(jpeg_info,jpeg_image);
cristyc8d69b12010-02-13 19:06:26 +00001950 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00001951 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00001952 else
cristy87e73ab2010-02-12 01:59:12 +00001953 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00001954 }
1955 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00001956 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00001957 jpeg_image=DestroyImage(jpeg_image);
1958 }
1959 jpeg_info=DestroyImageInfo(jpeg_info);
1960 }
cristy3ed852e2009-09-05 21:47:34 +00001961 if ((image_info->compression != LosslessJPEGCompression) &&
1962 (image->quality <= 100))
1963 {
1964 if (image->quality == UndefinedCompressionQuality)
1965 jpeg_set_quality(&jpeg_info,92,MagickTrue);
1966 else
1967 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
1968 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
1970 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00001971 }
1972 else
1973 {
1974#if !defined(C_LOSSLESS_SUPPORTED)
1975 jpeg_set_quality(&jpeg_info,100,MagickTrue);
1976 if (image->debug != MagickFalse)
1977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
1978#else
1979 if (image->quality < 100)
1980 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1981 CoderWarning,"LosslessToLossyJPEGConversion",image->filename);
1982 else
1983 {
1984 int
1985 point_transform,
1986 predictor;
1987
1988 predictor=image->quality/100; /* range 1-7 */
1989 point_transform=image->quality % 20; /* range 0-15 */
1990 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
1991 if (image->debug != MagickFalse)
1992 {
1993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1994 "Compression: lossless");
1995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1996 "Predictor: %d",predictor);
1997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1998 "Point Transform: %d",point_transform);
1999 }
2000 }
2001#endif
2002 }
2003 sampling_factor=(const char *) NULL;
2004 value=GetImageProperty(image,"jpeg:sampling-factor");
2005 if (value != (char *) NULL)
2006 {
2007 sampling_factor=value;
2008 if (image->debug != MagickFalse)
2009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2010 " Input sampling-factors=%s",sampling_factor);
2011 }
2012 if (image_info->sampling_factor != (char *) NULL)
2013 sampling_factor=image_info->sampling_factor;
2014 if (sampling_factor == (const char *) NULL)
2015 {
2016 if (image->quality >= 90)
2017 for (i=0; i < MAX_COMPONENTS; i++)
2018 {
2019 jpeg_info.comp_info[i].h_samp_factor=1;
2020 jpeg_info.comp_info[i].v_samp_factor=1;
2021 }
2022 }
2023 else
2024 {
2025 char
2026 **factors;
2027
2028 GeometryInfo
2029 geometry_info;
2030
2031 MagickStatusType
2032 flags;
2033
2034 /*
2035 Set sampling factor.
2036 */
2037 i=0;
2038 factors=SamplingFactorToList(sampling_factor);
2039 if (factors != (char **) NULL)
2040 {
2041 for (i=0; i < MAX_COMPONENTS; i++)
2042 {
2043 if (factors[i] == (char *) NULL)
2044 break;
2045 flags=ParseGeometry(factors[i],&geometry_info);
2046 if ((flags & SigmaValue) == 0)
2047 geometry_info.sigma=geometry_info.rho;
2048 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2049 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2050 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2051 }
2052 factors=(char **) RelinquishMagickMemory(factors);
2053 }
2054 for ( ; i < MAX_COMPONENTS; i++)
2055 {
2056 jpeg_info.comp_info[i].h_samp_factor=1;
2057 jpeg_info.comp_info[i].v_samp_factor=1;
2058 }
2059 }
2060 if (jpeg_info.input_components == 1)
2061 for (i=0; i < MAX_COMPONENTS; i++)
2062 {
2063 jpeg_info.comp_info[i].h_samp_factor=1;
2064 jpeg_info.comp_info[i].v_samp_factor=1;
2065 }
2066 jpeg_start_compress(&jpeg_info,MagickTrue);
2067 if (image->debug != MagickFalse)
2068 {
2069 if (image->storage_class == PseudoClass)
2070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2071 "Storage class: PseudoClass");
2072 else
2073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2074 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2076 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002077 if (image->colors != 0)
2078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002079 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002080 else
2081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2082 "Number of colors: unspecified");
2083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2084 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2085 switch (image->colorspace)
2086 {
2087 case CMYKColorspace:
2088 {
2089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2090 "Storage class: DirectClass");
2091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2092 "Colorspace: CMYK");
2093 break;
2094 }
2095 case YCbCrColorspace:
2096 case Rec601YCbCrColorspace:
2097 case Rec709YCbCrColorspace:
2098 {
2099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2100 "Colorspace: YCbCr");
2101 break;
2102 }
2103 default:
2104 break;
2105 }
2106 switch (image->colorspace)
2107 {
2108 case CMYKColorspace:
2109 {
2110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2111 "Colorspace: CMYK");
2112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2113 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2114 jpeg_info.comp_info[0].h_samp_factor,
2115 jpeg_info.comp_info[0].v_samp_factor,
2116 jpeg_info.comp_info[1].h_samp_factor,
2117 jpeg_info.comp_info[1].v_samp_factor,
2118 jpeg_info.comp_info[2].h_samp_factor,
2119 jpeg_info.comp_info[2].v_samp_factor,
2120 jpeg_info.comp_info[3].h_samp_factor,
2121 jpeg_info.comp_info[3].v_samp_factor);
2122 break;
2123 }
2124 case GRAYColorspace:
2125 case Rec601LumaColorspace:
2126 case Rec709LumaColorspace:
2127 {
2128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2129 "Colorspace: GRAY");
2130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2131 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2132 jpeg_info.comp_info[0].v_samp_factor);
2133 break;
2134 }
2135 case RGBColorspace:
2136 {
2137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2138 "Image colorspace is RGB");
2139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2140 "Sampling factors: %dx%d,%dx%d,%dx%d",
2141 jpeg_info.comp_info[0].h_samp_factor,
2142 jpeg_info.comp_info[0].v_samp_factor,
2143 jpeg_info.comp_info[1].h_samp_factor,
2144 jpeg_info.comp_info[1].v_samp_factor,
2145 jpeg_info.comp_info[2].h_samp_factor,
2146 jpeg_info.comp_info[2].v_samp_factor);
2147 break;
2148 }
2149 case YCbCrColorspace:
2150 case Rec601YCbCrColorspace:
2151 case Rec709YCbCrColorspace:
2152 {
2153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2154 "Colorspace: YCbCr");
2155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2156 "Sampling factors: %dx%d,%dx%d,%dx%d",
2157 jpeg_info.comp_info[0].h_samp_factor,
2158 jpeg_info.comp_info[0].v_samp_factor,
2159 jpeg_info.comp_info[1].h_samp_factor,
2160 jpeg_info.comp_info[1].v_samp_factor,
2161 jpeg_info.comp_info[2].h_samp_factor,
2162 jpeg_info.comp_info[2].v_samp_factor);
2163 break;
2164 }
2165 default:
2166 {
2167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2168 image->colorspace);
2169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2170 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2171 jpeg_info.comp_info[0].h_samp_factor,
2172 jpeg_info.comp_info[0].v_samp_factor,
2173 jpeg_info.comp_info[1].h_samp_factor,
2174 jpeg_info.comp_info[1].v_samp_factor,
2175 jpeg_info.comp_info[2].h_samp_factor,
2176 jpeg_info.comp_info[2].v_samp_factor,
2177 jpeg_info.comp_info[3].h_samp_factor,
2178 jpeg_info.comp_info[3].v_samp_factor);
2179 break;
2180 }
2181 }
2182 }
2183 /*
2184 Write JPEG profiles.
2185 */
2186 value=GetImageProperty(image,"comment");
2187 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002188 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002189 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2190 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2191 if (image->profiles != (void *) NULL)
2192 WriteProfile(&jpeg_info,image);
2193 /*
2194 Convert MIFF to JPEG raster pixels.
2195 */
2196 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2197 jpeg_info.input_components*sizeof(*jpeg_pixels));
2198 if (jpeg_pixels == (JSAMPLE *) NULL)
2199 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2200 if (setjmp(error_manager.error_recovery) != 0)
2201 {
2202 jpeg_destroy_compress(&jpeg_info);
2203 if (jpeg_pixels != (unsigned char *) NULL)
2204 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2205 (void) CloseBlob(image);
2206 return(MagickFalse);
2207 }
2208 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002209 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002210 {
cristy3ed852e2009-09-05 21:47:34 +00002211 if ((jpeg_info.in_color_space == JCS_RGB) ||
2212 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002213 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002214 {
cristy4c08aed2011-07-01 19:47:50 +00002215 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002216 *p;
2217
cristybb503372010-05-27 20:51:26 +00002218 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002219 x;
2220
2221 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002222 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002223 break;
2224 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002225 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002226 {
cristy4c08aed2011-07-01 19:47:50 +00002227 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2228 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2229 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002230 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002231 }
2232 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002233 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2234 image->rows);
2235 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002236 break;
2237 }
2238 else
cristye90d7402010-03-14 18:21:29 +00002239 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002240 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002241 {
cristy4c08aed2011-07-01 19:47:50 +00002242 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002243 *p;
2244
cristybb503372010-05-27 20:51:26 +00002245 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002246 x;
2247
2248 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002249 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002250 break;
2251 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002252 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002253 {
cristy4c08aed2011-07-01 19:47:50 +00002254 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristyed231572011-07-14 02:18:59 +00002255 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002256 }
2257 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002258 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2259 image->rows);
2260 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002261 break;
2262 }
2263 else
cristybb503372010-05-27 20:51:26 +00002264 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002265 {
cristy4c08aed2011-07-01 19:47:50 +00002266 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002267 *p;
2268
cristybb503372010-05-27 20:51:26 +00002269 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002270 x;
2271
2272 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002273 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002274 break;
2275 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002276 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002277 {
2278 /*
2279 Convert DirectClass packets to contiguous CMYK scanlines.
2280 */
2281 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002282 GetPixelRed(image,p))));
cristye90d7402010-03-14 18:21:29 +00002283 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002284 GetPixelGreen(image,p))));
cristye90d7402010-03-14 18:21:29 +00002285 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002286 GetPixelBlue(image,p))));
cristye90d7402010-03-14 18:21:29 +00002287 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002288 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002289 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002290 }
2291 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002292 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2293 image->rows);
2294 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002295 break;
2296 }
2297 }
2298 else
2299 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002300 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002301 {
cristy4c08aed2011-07-01 19:47:50 +00002302 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002303 *p;
2304
cristybb503372010-05-27 20:51:26 +00002305 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002306 x;
2307
2308 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002309 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002310 break;
2311 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002312 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002313 {
cristy4c08aed2011-07-01 19:47:50 +00002314 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >>
cristye90d7402010-03-14 18:21:29 +00002315 4);
cristyed231572011-07-14 02:18:59 +00002316 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002317 }
2318 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002319 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2320 image->rows);
2321 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002322 break;
2323 }
2324 else
2325 if ((jpeg_info.in_color_space == JCS_RGB) ||
2326 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002327 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002328 {
cristy4c08aed2011-07-01 19:47:50 +00002329 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002330 *p;
2331
cristybb503372010-05-27 20:51:26 +00002332 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002333 x;
2334
2335 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002336 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002337 break;
2338 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002339 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002340 {
cristy4c08aed2011-07-01 19:47:50 +00002341 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2342 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2343 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002344 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002345 }
2346 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002347 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002348 image->rows);
cristye90d7402010-03-14 18:21:29 +00002349 if (status == MagickFalse)
2350 break;
2351 }
2352 else
cristybb503372010-05-27 20:51:26 +00002353 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002354 {
cristy4c08aed2011-07-01 19:47:50 +00002355 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002356 *p;
2357
cristybb503372010-05-27 20:51:26 +00002358 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002359 x;
2360
2361 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002362 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002363 break;
2364 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002365 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002366 {
2367 /*
2368 Convert DirectClass packets to contiguous CMYK scanlines.
2369 */
cristye90d7402010-03-14 18:21:29 +00002370 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002371 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002372 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002373 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002374 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002375 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002376 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002377 GetPixelBlack(image,p)) >> 4));
cristyed231572011-07-14 02:18:59 +00002378 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002379 }
2380 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002381 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002382 image->rows);
cristye90d7402010-03-14 18:21:29 +00002383 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002384 break;
2385 }
cristybb503372010-05-27 20:51:26 +00002386 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002387 jpeg_finish_compress(&jpeg_info);
2388 /*
2389 Relinquish resources.
2390 */
2391 jpeg_destroy_compress(&jpeg_info);
2392 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2393 (void) CloseBlob(image);
2394 return(MagickTrue);
2395}
2396#endif