blob: f8782da7ef3f12bb7417331008bbe87c72be15c7 [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
cristye23ec9d2011-08-16 18:15:40 +0000576 const StringInfo
577 *previous_profile;
578
cristy3ed852e2009-09-05 21:47:34 +0000579 ErrorManager
580 *error_manager;
581
582 Image
583 *image;
584
585 int
586 marker;
587
588 MagickBooleanType
589 status;
590
cristybb503372010-05-27 20:51:26 +0000591 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000592 i;
593
594 register unsigned char
595 *p;
596
597 size_t
598 length;
599
600 StringInfo
601 *profile;
602
603 /*
604 Read generic profile.
605 */
cristybb503372010-05-27 20:51:26 +0000606 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000607 length+=(size_t) GetCharacter(jpeg_info);
608 if (length <= 2)
609 return(MagickTrue);
610 length-=2;
611 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000612 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000613 error_manager=(ErrorManager *) jpeg_info->client_data;
614 image=error_manager->image;
615 profile=AcquireStringInfo(length);
616 if (profile == (StringInfo *) NULL)
617 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
618 image->filename);
619 p=GetStringInfoDatum(profile);
cristy08f255a2011-08-17 01:23:06 +0000620 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000621 *p++=(unsigned char) GetCharacter(jpeg_info);
622 if (marker == 1)
623 {
624 p=GetStringInfoDatum(profile);
625 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
626 (void) CopyMagickString(name,"exif",MaxTextExtent);
627 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
628 {
cristybb503372010-05-27 20:51:26 +0000629 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000630 j;
631
632 /*
633 Extract namespace from XMP profile.
634 */
635 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000636 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000637 {
638 if (*p == '\0')
639 break;
640 p++;
641 }
cristybb503372010-05-27 20:51:26 +0000642 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000643 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
644 (void) CopyMagickString(name,"xmp",MaxTextExtent);
645 }
646 }
cristye23ec9d2011-08-16 18:15:40 +0000647 previous_profile=GetImageProfile(image,name);
648 if (previous_profile != (const StringInfo *) NULL)
cristy08f255a2011-08-17 01:23:06 +0000649 {
650 SetStringInfoLength(profile,GetStringInfoLength(profile)+
651 GetStringInfoLength(previous_profile));
652 (void) memcpy(GetStringInfoDatum(profile),GetStringInfoDatum(profile)+
653 GetStringInfoLength(previous_profile),GetStringInfoLength(profile));
654 (void) memcpy(GetStringInfoDatum(profile),
655 GetStringInfoDatum(previous_profile),
656 GetStringInfoLength(previous_profile));
657 }
cristy3ed852e2009-09-05 21:47:34 +0000658 status=SetImageProfile(image,name,profile);
659 profile=DestroyStringInfo(profile);
660 if (status == MagickFalse)
661 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
662 image->filename);
663 if (image->debug != MagickFalse)
664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000665 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000666 return(MagickTrue);
667}
668
cristyf2faecf2010-05-28 19:19:36 +0000669static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000670{
671 SourceManager
672 *source;
673
674 if (number_bytes <= 0)
675 return;
676 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000677 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000678 {
cristyf2faecf2010-05-28 19:19:36 +0000679 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000680 (void) FillInputBuffer(cinfo);
681 }
cristy4cb162a2010-05-30 03:04:47 +0000682 source->manager.next_input_byte+=number_bytes;
683 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000684}
685
686static void TerminateSource(j_decompress_ptr cinfo)
687{
688 cinfo=cinfo;
689}
690
691static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
692{
693 SourceManager
694 *source;
695
696 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
697 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
698 source=(SourceManager *) cinfo->src;
699 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
700 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
701 source=(SourceManager *) cinfo->src;
702 source->manager.init_source=InitializeSource;
703 source->manager.fill_input_buffer=FillInputBuffer;
704 source->manager.skip_input_data=SkipInputData;
705 source->manager.resync_to_restart=jpeg_resync_to_restart;
706 source->manager.term_source=TerminateSource;
707 source->manager.bytes_in_buffer=0;
708 source->manager.next_input_byte=NULL;
709 source->image=image;
710}
711
712static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
713 Image *image)
714{
715 image->quality=UndefinedCompressionQuality;
716#if defined(D_PROGRESSIVE_SUPPORTED)
717 if (image->compression == LosslessJPEGCompression)
718 {
719 image->quality=100;
720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
721 "Quality: 100 (lossless)");
722 }
723 else
724#endif
725 {
cristybb503372010-05-27 20:51:26 +0000726 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000727 j,
728 qvalue,
729 sum;
730
cristybb503372010-05-27 20:51:26 +0000731 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000732 i;
733
734 /*
735 Determine the JPEG compression quality from the quantization tables.
736 */
737 sum=0;
738 for (i=0; i < NUM_QUANT_TBLS; i++)
739 {
740 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
741 for (j=0; j < DCTSIZE2; j++)
742 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
743 }
744 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
745 (jpeg_info->quant_tbl_ptrs[1] != NULL))
746 {
cristybb503372010-05-27 20:51:26 +0000747 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000748 hash[101] =
749 {
750 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
751 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
752 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
753 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
754 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
755 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
756 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
757 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
758 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
759 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
760 0
761 },
762 sums[101] =
763 {
764 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
765 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
766 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
767 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
768 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
769 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
770 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
771 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
772 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
773 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
774 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
775 128, 0
776 };
777
cristybb503372010-05-27 20:51:26 +0000778 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000779 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
780 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
781 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
782 for (i=0; i < 100; i++)
783 {
784 if ((qvalue < hash[i]) && (sum < sums[i]))
785 continue;
cristyb6c017e2010-01-13 19:11:39 +0000786 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000787 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000788 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000790 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000791 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000792 break;
793 }
794 }
795 else
796 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
797 {
cristybb503372010-05-27 20:51:26 +0000798 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000799 hash[101] =
800 {
801 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
802 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
803 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
804 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
805 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
806 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
807 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
808 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
809 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
810 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
811 0
812 },
813 sums[101] =
814 {
815 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
816 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
817 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
818 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
819 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
820 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
821 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
822 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
823 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
824 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
825 667, 592, 518, 441, 369, 292, 221, 151, 86,
826 64, 0
827 };
828
cristybb503372010-05-27 20:51:26 +0000829 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000830 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
831 for (i=0; i < 100; i++)
832 {
833 if ((qvalue < hash[i]) && (sum < sums[i]))
834 continue;
cristyb6c017e2010-01-13 19:11:39 +0000835 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000836 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000837 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000839 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000840 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000841 break;
842 }
843 }
844 }
845}
846
847static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image)
848{
849 char
850 sampling_factor[MaxTextExtent];
851
852 switch (jpeg_info->out_color_space)
853 {
854 case JCS_CMYK:
855 {
856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000857 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000858 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
859 jpeg_info->comp_info[0].v_samp_factor,
860 jpeg_info->comp_info[1].h_samp_factor,
861 jpeg_info->comp_info[1].v_samp_factor,
862 jpeg_info->comp_info[2].h_samp_factor,
863 jpeg_info->comp_info[2].v_samp_factor,
864 jpeg_info->comp_info[3].h_samp_factor,
865 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000866 break;
cristy3ed852e2009-09-05 21:47:34 +0000867 }
868 case JCS_GRAYSCALE:
869 {
870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
871 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000872 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000873 jpeg_info->comp_info[0].h_samp_factor,
874 jpeg_info->comp_info[0].v_samp_factor);
875 break;
876 }
877 case JCS_RGB:
878 {
879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000880 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000881 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
882 jpeg_info->comp_info[0].v_samp_factor,
883 jpeg_info->comp_info[1].h_samp_factor,
884 jpeg_info->comp_info[1].v_samp_factor,
885 jpeg_info->comp_info[2].h_samp_factor,
886 jpeg_info->comp_info[2].v_samp_factor);
887 break;
888 }
889 default:
890 {
891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
892 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000893 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000894 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
895 jpeg_info->comp_info[0].v_samp_factor,
896 jpeg_info->comp_info[1].h_samp_factor,
897 jpeg_info->comp_info[1].v_samp_factor,
898 jpeg_info->comp_info[2].h_samp_factor,
899 jpeg_info->comp_info[2].v_samp_factor,
900 jpeg_info->comp_info[3].h_samp_factor,
901 jpeg_info->comp_info[3].v_samp_factor);
902 break;
903 }
904 }
905 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor);
cristye90d7402010-03-14 18:21:29 +0000906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
907 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000908}
909
910static Image *ReadJPEGImage(const ImageInfo *image_info,
911 ExceptionInfo *exception)
912{
913 char
914 value[MaxTextExtent];
915
cristycaec74e2009-09-14 02:20:04 +0000916 const char
cristy11151212009-09-14 13:10:15 +0000917 *option;
cristycaec74e2009-09-14 02:20:04 +0000918
cristy3ed852e2009-09-05 21:47:34 +0000919 ErrorManager
920 error_manager;
921
cristy3ed852e2009-09-05 21:47:34 +0000922 Image
923 *image;
924
cristy3ed852e2009-09-05 21:47:34 +0000925 JSAMPLE
926 *jpeg_pixels;
927
928 JSAMPROW
929 scanline[1];
930
931 MagickBooleanType
932 debug,
933 status;
934
935 MagickSizeType
936 number_pixels;
937
cristy4c08aed2011-07-01 19:47:50 +0000938 Quantum
939 index;
940
cristybb503372010-05-27 20:51:26 +0000941 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000942 i;
943
944 struct jpeg_decompress_struct
945 jpeg_info;
946
947 struct jpeg_error_mgr
948 jpeg_error;
949
950 register JSAMPLE
951 *p;
952
cristybb503372010-05-27 20:51:26 +0000953 size_t
cristy3ed852e2009-09-05 21:47:34 +0000954 precision,
955 units;
956
cristy524222d2011-04-25 00:37:06 +0000957 ssize_t
958 y;
959
cristy3ed852e2009-09-05 21:47:34 +0000960 /*
961 Open image file.
962 */
963 assert(image_info != (const ImageInfo *) NULL);
964 assert(image_info->signature == MagickSignature);
965 if (image_info->debug != MagickFalse)
966 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
967 image_info->filename);
968 assert(exception != (ExceptionInfo *) NULL);
969 assert(exception->signature == MagickSignature);
970 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +0000971 (void) debug;
cristy3ed852e2009-09-05 21:47:34 +0000972 image=AcquireImage(image_info);
973 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
974 if (status == MagickFalse)
975 {
976 image=DestroyImageList(image);
977 return((Image *) NULL);
978 }
979 /*
980 Initialize JPEG parameters.
981 */
cristy91044972011-04-22 14:21:16 +0000982 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +0000983 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
984 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
985 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +0000986 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +0000987 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
988 jpeg_pixels=(JSAMPLE *) NULL;
989 error_manager.image=image;
990 if (setjmp(error_manager.error_recovery) != 0)
991 {
992 jpeg_destroy_decompress(&jpeg_info);
993 (void) CloseBlob(image);
994 number_pixels=(MagickSizeType) image->columns*image->rows;
995 if (number_pixels != 0)
996 return(GetFirstImageInList(image));
cristy195f6d12009-11-05 18:16:03 +0000997 InheritException(exception,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +0000998 return(DestroyImage(image));
999 }
1000 jpeg_info.client_data=(void *) &error_manager;
1001 jpeg_create_decompress(&jpeg_info);
1002 JPEGSourceManager(&jpeg_info,image);
1003 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
1004 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
1005 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1006 for (i=1; i < 16; i++)
1007 if ((i != 2) && (i != 13) && (i != 14))
1008 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +00001009 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +00001010 if ((image_info->colorspace == YCbCrColorspace) ||
1011 (image_info->colorspace == Rec601YCbCrColorspace) ||
1012 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001013 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +00001014 if (IsITUFaxImage(image) != MagickFalse)
1015 {
1016 image->colorspace=LabColorspace;
1017 jpeg_info.out_color_space=JCS_YCbCr;
1018 }
1019 else
1020 if (jpeg_info.out_color_space == JCS_CMYK)
1021 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001022 /*
1023 Set image resolution.
1024 */
1025 units=0;
1026 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1027 (jpeg_info.Y_density != 1))
1028 {
1029 image->x_resolution=(double) jpeg_info.X_density;
1030 image->y_resolution=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001031 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001032 }
1033 if (units == 1)
1034 image->units=PixelsPerInchResolution;
1035 if (units == 2)
1036 image->units=PixelsPerCentimeterResolution;
1037 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +00001038 option=GetImageOption(image_info,"jpeg:size");
1039 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001040 {
1041 double
1042 scale_factor;
1043
cristycaec74e2009-09-14 02:20:04 +00001044 GeometryInfo
1045 geometry_info;
1046
cristy0adb4f92009-11-28 18:08:51 +00001047 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001048 flags;
1049
cristy3ed852e2009-09-05 21:47:34 +00001050 /*
cristycaec74e2009-09-14 02:20:04 +00001051 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001052 */
cristy11151212009-09-14 13:10:15 +00001053 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001054 if ((flags & SigmaValue) == 0)
1055 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001056 jpeg_calc_output_dimensions(&jpeg_info);
1057 image->magick_columns=jpeg_info.output_width;
1058 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001059 scale_factor=1.0;
1060 if (geometry_info.rho != 0.0)
1061 scale_factor=jpeg_info.output_width/geometry_info.rho;
1062 if ((geometry_info.sigma != 0.0) &&
1063 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1064 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001065 jpeg_info.scale_num=1U;
1066 jpeg_info.scale_denom=(unsigned int) scale_factor;
1067 jpeg_calc_output_dimensions(&jpeg_info);
1068 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1070 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001071 }
cristybb503372010-05-27 20:51:26 +00001072 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001073#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1074#if defined(D_LOSSLESS_SUPPORTED)
1075 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1076 JPEGInterlace : NoInterlace;
1077 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1078 LosslessJPEGCompression : JPEGCompression;
1079 if (jpeg_info.data_precision > 8)
1080 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1081 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1082 image->filename);
1083 if (jpeg_info.data_precision == 16)
1084 jpeg_info.data_precision=12;
1085#else
1086 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1087 NoInterlace;
1088 image->compression=JPEGCompression;
1089#endif
1090#else
1091 image->compression=JPEGCompression;
1092 image->interlace=JPEGInterlace;
1093#endif
1094 if ((image_info->colors > 8) && (image_info->colors <= 256))
1095 {
1096 /*
1097 Let the JPEG library quantize for us.
1098 */
1099 jpeg_info.quantize_colors=MagickTrue;
1100 jpeg_info.desired_number_of_colors=(int) image_info->colors;
1101 }
cristy1b8d4462009-10-27 13:46:56 +00001102 option=GetImageOption(image_info,"jpeg:block-smoothing");
1103 if (option != (const char *) NULL)
1104 {
1105 jpeg_info.do_block_smoothing=MagickFalse;
1106 if (IsMagickTrue(option) != MagickFalse)
1107 jpeg_info.do_block_smoothing=MagickTrue;
1108 }
cristy787d4352010-03-06 13:55:58 +00001109 option=GetImageOption(image_info,"jpeg:dct-method");
1110 if (option != (const char *) NULL)
1111 switch (*option)
1112 {
1113 case 'D':
1114 case 'd':
1115 {
1116 if (LocaleCompare(option,"default") == 0)
1117 jpeg_info.dct_method=JDCT_DEFAULT;
1118 break;
1119 }
1120 case 'F':
1121 case 'f':
1122 {
1123 if (LocaleCompare(option,"fastest") == 0)
1124 jpeg_info.dct_method=JDCT_FASTEST;
1125 if (LocaleCompare(option,"float") == 0)
1126 jpeg_info.dct_method=JDCT_FLOAT;
1127 break;
1128 }
1129 case 'I':
1130 case 'i':
1131 {
1132 if (LocaleCompare(option,"ifast") == 0)
1133 jpeg_info.dct_method=JDCT_IFAST;
1134 if (LocaleCompare(option,"islow") == 0)
1135 jpeg_info.dct_method=JDCT_ISLOW;
1136 break;
1137 }
1138 }
cristy1b8d4462009-10-27 13:46:56 +00001139 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1140 if (option != (const char *) NULL)
1141 {
1142 jpeg_info.do_fancy_upsampling=MagickFalse;
1143 if (IsMagickTrue(option) != MagickFalse)
1144 jpeg_info.do_fancy_upsampling=MagickTrue;
1145 }
cristy3ed852e2009-09-05 21:47:34 +00001146 (void) jpeg_start_decompress(&jpeg_info);
1147 image->columns=jpeg_info.output_width;
1148 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001149 image->depth=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001150 if (jpeg_info.out_color_space == JCS_YCbCr)
1151 image->colorspace=YCbCrColorspace;
1152 if (jpeg_info.out_color_space == JCS_CMYK)
1153 image->colorspace=CMYKColorspace;
1154 if ((image_info->colors != 0) && (image_info->colors <= 256))
1155 if (AcquireImageColormap(image,image_info->colors) == MagickFalse)
1156 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1157 if ((jpeg_info.output_components == 1) &&
1158 (jpeg_info.quantize_colors == MagickFalse))
1159 {
cristybb503372010-05-27 20:51:26 +00001160 size_t
cristy3ed852e2009-09-05 21:47:34 +00001161 colors;
1162
cristybb503372010-05-27 20:51:26 +00001163 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy3ed852e2009-09-05 21:47:34 +00001164 if (AcquireImageColormap(image,colors) == MagickFalse)
1165 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1166 }
1167 if (image->debug != MagickFalse)
1168 {
1169 if (image->interlace != NoInterlace)
1170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1171 "Interlace: progressive");
1172 else
1173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1174 "Interlace: nonprogressive");
1175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1176 (int) jpeg_info.data_precision);
1177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1178 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1179 }
1180 JPEGSetImageQuality(&jpeg_info,image);
1181 JPEGSetImageSamplingFactor(&jpeg_info,image);
cristyb51dff52011-05-19 16:55:47 +00001182 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001183 jpeg_info.out_color_space);
1184 (void) SetImageProperty(image,"jpeg:colorspace",value);
1185 if (image_info->ping != MagickFalse)
1186 {
1187 jpeg_destroy_decompress(&jpeg_info);
1188 (void) CloseBlob(image);
1189 return(GetFirstImageInList(image));
1190 }
1191 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1192 jpeg_info.output_components*sizeof(JSAMPLE));
1193 if (jpeg_pixels == (JSAMPLE *) NULL)
1194 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1195 /*
1196 Convert JPEG pixels to pixel packets.
1197 */
1198 if (setjmp(error_manager.error_recovery) != 0)
1199 {
1200 if (jpeg_pixels != (unsigned char *) NULL)
1201 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1202 jpeg_destroy_decompress(&jpeg_info);
1203 (void) CloseBlob(image);
1204 number_pixels=(MagickSizeType) image->columns*image->rows;
1205 if (number_pixels != 0)
1206 return(GetFirstImageInList(image));
1207 return(DestroyImage(image));
1208 }
1209 if (jpeg_info.quantize_colors != MagickFalse)
1210 {
cristybb503372010-05-27 20:51:26 +00001211 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001212 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001213 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001214 {
1215 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1216 image->colormap[i].green=image->colormap[i].red;
1217 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001218 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001219 }
1220 else
cristybb503372010-05-27 20:51:26 +00001221 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001222 {
1223 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1224 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1225 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001226 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001227 }
1228 }
1229 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001230 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001231 {
cristybb503372010-05-27 20:51:26 +00001232 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001233 x;
1234
cristy4c08aed2011-07-01 19:47:50 +00001235 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001236 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001237
1238 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1239 {
1240 (void) ThrowMagickException(exception,GetMagickModule(),
1241 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1242 continue;
1243 }
1244 p=jpeg_pixels;
1245 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001246 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001247 break;
cristy3ed852e2009-09-05 21:47:34 +00001248 if (jpeg_info.data_precision > 8)
1249 {
1250 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001251 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001252 {
cristybb503372010-05-27 20:51:26 +00001253 size_t
cristy3ed852e2009-09-05 21:47:34 +00001254 pixel;
1255
1256 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001257 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001258 else
cristybb503372010-05-27 20:51:26 +00001259 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristya0511732010-02-17 02:38:35 +00001260 index=ConstrainColormapIndex(image,pixel);
cristy4c08aed2011-07-01 19:47:50 +00001261 SetPixelIndex(image,index,q);
1262 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001263 p++;
cristyed231572011-07-14 02:18:59 +00001264 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001265 }
1266 else
1267 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001268 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001269 {
cristy4c08aed2011-07-01 19:47:50 +00001270 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1271 (GETJSAMPLE(*p++) << 4)),q);
1272 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1273 (GETJSAMPLE(*p++) << 4)),q);
1274 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1275 (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 else
cristybb503372010-05-27 20:51:26 +00001280 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001281 {
cristy4c08aed2011-07-01 19:47:50 +00001282 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1283 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1284 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1285 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1286 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1287 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1288 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1289 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1290 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001291 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001292 }
1293 }
1294 else
1295 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001296 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001297 {
cristybb503372010-05-27 20:51:26 +00001298 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p));
cristy4c08aed2011-07-01 19:47:50 +00001299 SetPixelIndex(image,index,q);
1300 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001301 p++;
cristyed231572011-07-14 02:18:59 +00001302 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001303 }
1304 else
1305 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001306 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001307 {
cristy4c08aed2011-07-01 19:47:50 +00001308 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1309 GETJSAMPLE(*p++)),q);
1310 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1311 GETJSAMPLE(*p++)),q);
1312 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1313 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 else
cristybb503372010-05-27 20:51:26 +00001318 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001319 {
cristy4c08aed2011-07-01 19:47:50 +00001320 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1321 (unsigned char) GETJSAMPLE(*p++)),q);
1322 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1323 (unsigned char) GETJSAMPLE(*p++)),q);
1324 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1325 (unsigned char) GETJSAMPLE(*p++)),q);
1326 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1327 (unsigned char) GETJSAMPLE(*p++)),q);
1328 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001329 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001330 }
1331 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1332 break;
cristy524222d2011-04-25 00:37:06 +00001333 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1334 image->rows);
1335 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001336 {
1337 jpeg_abort_decompress(&jpeg_info);
1338 break;
1339 }
cristy3ed852e2009-09-05 21:47:34 +00001340 }
cristyd28b1dd2011-05-14 20:30:38 +00001341 if (status != MagickFalse)
1342 {
1343 error_manager.finished=MagickTrue;
1344 if (setjmp(error_manager.error_recovery) == 0)
1345 (void) jpeg_finish_decompress(&jpeg_info);
1346 }
cristy3ed852e2009-09-05 21:47:34 +00001347 /*
1348 Free jpeg resources.
1349 */
cristy3ed852e2009-09-05 21:47:34 +00001350 jpeg_destroy_decompress(&jpeg_info);
1351 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1352 (void) CloseBlob(image);
1353 return(GetFirstImageInList(image));
1354}
1355#endif
1356
1357/*
1358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1359% %
1360% %
1361% %
1362% R e g i s t e r J P E G I m a g e %
1363% %
1364% %
1365% %
1366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1367%
1368% RegisterJPEGImage() adds properties for the JPEG image format to
1369% the list of supported formats. The properties include the image format
1370% tag, a method to read and/or write the format, whether the format
1371% supports the saving of more than one frame to the same file or blob,
1372% whether the format supports native in-memory I/O, and a brief
1373% description of the format.
1374%
1375% The format of the RegisterJPEGImage method is:
1376%
cristybb503372010-05-27 20:51:26 +00001377% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001378%
1379*/
cristybb503372010-05-27 20:51:26 +00001380ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001381{
1382 char
1383 version[MaxTextExtent];
1384
1385 MagickInfo
1386 *entry;
1387
1388 static const char
cristy7138c592009-09-08 13:58:52 +00001389 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001390
1391 *version='\0';
1392#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001393 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001394#endif
1395 entry=SetMagickInfo("JPEG");
1396 entry->thread_support=NoThreadSupport;
1397#if defined(MAGICKCORE_JPEG_DELEGATE)
1398 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1399 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1400#endif
1401 entry->magick=(IsImageFormatHandler *) IsJPEG;
1402 entry->adjoin=MagickFalse;
1403 entry->description=ConstantString(description);
1404 if (*version != '\0')
1405 entry->version=ConstantString(version);
1406 entry->module=ConstantString("JPEG");
1407 (void) RegisterMagickInfo(entry);
1408 entry=SetMagickInfo("JPG");
1409 entry->thread_support=NoThreadSupport;
1410#if defined(MAGICKCORE_JPEG_DELEGATE)
1411 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1412 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1413#endif
1414 entry->adjoin=MagickFalse;
1415 entry->description=ConstantString(description);
1416 if (*version != '\0')
1417 entry->version=ConstantString(version);
1418 entry->module=ConstantString("JPEG");
1419 (void) RegisterMagickInfo(entry);
1420 entry=SetMagickInfo("PJPEG");
1421 entry->thread_support=NoThreadSupport;
1422#if defined(MAGICKCORE_JPEG_DELEGATE)
1423 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1424 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1425#endif
1426 entry->adjoin=MagickFalse;
1427 entry->description=ConstantString(description);
1428 if (*version != '\0')
1429 entry->version=ConstantString(version);
1430 entry->module=ConstantString("JPEG");
1431 (void) RegisterMagickInfo(entry);
1432 return(MagickImageCoderSignature);
1433}
1434
1435/*
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437% %
1438% %
1439% %
1440% U n r e g i s t e r J P E G I m a g e %
1441% %
1442% %
1443% %
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445%
1446% UnregisterJPEGImage() removes format registrations made by the
1447% JPEG module from the list of supported formats.
1448%
1449% The format of the UnregisterJPEGImage method is:
1450%
1451% UnregisterJPEGImage(void)
1452%
1453*/
1454ModuleExport void UnregisterJPEGImage(void)
1455{
1456 (void) UnregisterMagickInfo("PJPG");
1457 (void) UnregisterMagickInfo("JPEG");
1458 (void) UnregisterMagickInfo("JPG");
1459}
1460
1461#if defined(MAGICKCORE_JPEG_DELEGATE)
1462/*
1463%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1464% %
1465% %
1466% %
1467% W r i t e J P E G I m a g e %
1468% %
1469% %
1470% %
1471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1472%
1473% WriteJPEGImage() writes a JPEG image file and returns it. It
1474% allocates the memory necessary for the new Image structure and returns a
1475% pointer to the new image.
1476%
1477% The format of the WriteJPEGImage method is:
1478%
cristy91044972011-04-22 14:21:16 +00001479% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1480% Image *image)
cristy3ed852e2009-09-05 21:47:34 +00001481%
1482% A description of each parameter follows:
1483%
1484% o image_info: the image info.
1485%
1486% o jpeg_image: The image.
1487%
1488%
1489*/
1490
1491static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1492{
1493 DestinationManager
1494 *destination;
1495
1496 destination=(DestinationManager *) cinfo->dest;
1497 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1498 MaxBufferExtent,destination->buffer);
1499 if (destination->manager.free_in_buffer != MaxBufferExtent)
1500 ERREXIT(cinfo,JERR_FILE_WRITE);
1501 destination->manager.next_output_byte=destination->buffer;
1502 return(TRUE);
1503}
1504
1505static void InitializeDestination(j_compress_ptr cinfo)
1506{
1507 DestinationManager
1508 *destination;
1509
1510 destination=(DestinationManager *) cinfo->dest;
1511 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1512 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1513 destination->manager.next_output_byte=destination->buffer;
1514 destination->manager.free_in_buffer=MaxBufferExtent;
1515}
1516
1517static inline size_t MagickMin(const size_t x,const size_t y)
1518{
1519 if (x < y)
1520 return(x);
1521 return(y);
1522}
1523
1524static void TerminateDestination(j_compress_ptr cinfo)
1525{
1526 DestinationManager
1527 *destination;
1528
1529 destination=(DestinationManager *) cinfo->dest;
1530 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1531 {
1532 ssize_t
1533 count;
1534
1535 count=WriteBlob(destination->image,MaxBufferExtent-
1536 destination->manager.free_in_buffer,destination->buffer);
1537 if (count != (ssize_t)
1538 (MaxBufferExtent-destination->manager.free_in_buffer))
1539 ERREXIT(cinfo,JERR_FILE_WRITE);
1540 }
1541}
1542
1543static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1544{
1545 const char
1546 *name;
1547
1548 const StringInfo
1549 *profile;
1550
1551 MagickBooleanType
1552 iptc;
1553
cristybb503372010-05-27 20:51:26 +00001554 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001555 i;
1556
1557 size_t
cristy524222d2011-04-25 00:37:06 +00001558 length,
1559 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001560
1561 StringInfo
1562 *custom_profile;
1563
cristy3ed852e2009-09-05 21:47:34 +00001564 /*
1565 Save image profile as a APP marker.
1566 */
1567 iptc=MagickFalse;
1568 custom_profile=AcquireStringInfo(65535L);
1569 ResetImageProfileIterator(image);
1570 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1571 {
cristy109e5572010-09-16 18:38:17 +00001572 register unsigned char
1573 *p;
1574
cristy3ed852e2009-09-05 21:47:34 +00001575 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001576 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001577 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001578 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001579 {
1580 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1581 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1582 (unsigned int) length);
1583 }
1584 if (LocaleCompare(name,"ICC") == 0)
1585 {
1586 register unsigned char
1587 *p;
1588
1589 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001590 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001591 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001592 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001593 {
1594 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001595 p[12]=(unsigned char) ((i/65519L)+1);
1596 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1597 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1598 length);
1599 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1600 custom_profile),(unsigned int) (length+tag_length));
1601 }
1602 }
1603 if (((LocaleCompare(name,"IPTC") == 0) ||
1604 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1605 {
cristybb503372010-05-27 20:51:26 +00001606 size_t
cristy3ed852e2009-09-05 21:47:34 +00001607 roundup;
1608
1609 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001610 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001611 {
1612 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001613 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001614 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1615 {
1616 (void) memcpy(p,"Photoshop 3.0 ",14);
1617 tag_length=14;
1618 }
1619 else
1620 {
1621 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1622 tag_length=26;
1623 p[24]=(unsigned char) (length >> 8);
1624 p[25]=(unsigned char) (length & 0xff);
1625 }
1626 p[13]=0x00;
1627 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001628 if (roundup != 0)
1629 p[length+tag_length]='\0';
1630 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1631 custom_profile),(unsigned int) (length+tag_length+roundup));
1632 }
1633 }
1634 if (LocaleCompare(name,"XMP") == 0)
1635 {
1636 StringInfo
1637 *xmp_profile;
1638
1639 /*
1640 Add namespace to XMP profile.
1641 */
1642 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/");
1643 ConcatenateStringInfo(xmp_profile,profile);
1644 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001645 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001646 {
1647 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1648 jpeg_write_marker(jpeg_info,XML_MARKER,
1649 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1650 }
1651 xmp_profile=DestroyStringInfo(xmp_profile);
1652 }
cristye8c25f92010-06-03 00:53:06 +00001653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1654 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001655 name=GetNextImageProfile(image);
1656 }
1657 custom_profile=DestroyStringInfo(custom_profile);
1658}
1659
1660static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1661{
1662 DestinationManager
1663 *destination;
1664
1665 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1666 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1667 destination=(DestinationManager *) cinfo->dest;
1668 destination->manager.init_destination=InitializeDestination;
1669 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1670 destination->manager.term_destination=TerminateDestination;
1671 destination->image=image;
1672}
1673
1674static char **SamplingFactorToList(const char *text)
1675{
1676 char
1677 **textlist;
1678
1679 register char
1680 *q;
1681
1682 register const char
1683 *p;
1684
cristybb503372010-05-27 20:51:26 +00001685 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001686 i;
1687
cristybb503372010-05-27 20:51:26 +00001688 size_t
cristy3ed852e2009-09-05 21:47:34 +00001689 lines;
1690
1691 if (text == (char *) NULL)
1692 return((char **) NULL);
1693 /*
1694 Convert string to an ASCII list.
1695 */
1696 lines=1;
1697 for (p=text; *p != '\0'; p++)
1698 if (*p == ',')
1699 lines++;
1700 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1701 sizeof(*textlist));
1702 if (textlist == (char **) NULL)
1703 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1704 p=text;
cristybb503372010-05-27 20:51:26 +00001705 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001706 {
1707 for (q=(char *) p; *q != '\0'; q++)
1708 if (*q == ',')
1709 break;
1710 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1711 sizeof(*textlist[i]));
1712 if (textlist[i] == (char *) NULL)
1713 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1714 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1715 if (*q == '\r')
1716 q++;
1717 p=q+1;
1718 }
1719 textlist[i]=(char *) NULL;
1720 return(textlist);
1721}
1722
1723static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1724 Image *image)
1725{
1726 const char
1727 *option,
1728 *sampling_factor,
1729 *value;
1730
1731 ErrorManager
1732 error_manager;
1733
1734 JSAMPLE
1735 *jpeg_pixels;
1736
1737 JSAMPROW
1738 scanline[1];
1739
cristy3ed852e2009-09-05 21:47:34 +00001740 MagickBooleanType
1741 status;
1742
1743 register JSAMPLE
1744 *q;
1745
cristybb503372010-05-27 20:51:26 +00001746 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001747 i;
1748
cristy524222d2011-04-25 00:37:06 +00001749 ssize_t
1750 y;
1751
cristy3ed852e2009-09-05 21:47:34 +00001752 struct jpeg_compress_struct
1753 jpeg_info;
1754
1755 struct jpeg_error_mgr
1756 jpeg_error;
1757
1758 /*
1759 Open image file.
1760 */
1761 assert(image_info != (const ImageInfo *) NULL);
1762 assert(image_info->signature == MagickSignature);
1763 assert(image != (Image *) NULL);
1764 assert(image->signature == MagickSignature);
1765 if (image->debug != MagickFalse)
1766 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1767 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1768 if (status == MagickFalse)
1769 return(status);
1770 /*
1771 Initialize JPEG parameters.
1772 */
cristy91044972011-04-22 14:21:16 +00001773 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001774 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1775 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1776 jpeg_info.client_data=(void *) image;
1777 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001778 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00001779 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy3ed852e2009-09-05 21:47:34 +00001780 error_manager.image=image;
1781 jpeg_pixels=(JSAMPLE *) NULL;
1782 if (setjmp(error_manager.error_recovery) != 0)
1783 {
1784 jpeg_destroy_compress(&jpeg_info);
1785 (void) CloseBlob(image);
1786 return(MagickFalse);
1787 }
1788 jpeg_info.client_data=(void *) &error_manager;
1789 jpeg_create_compress(&jpeg_info);
1790 JPEGDestinationManager(&jpeg_info,image);
1791 if ((image->columns != (unsigned int) image->columns) ||
1792 (image->rows != (unsigned int) image->rows))
1793 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1794 jpeg_info.image_width=(unsigned int) image->columns;
1795 jpeg_info.image_height=(unsigned int) image->rows;
1796 jpeg_info.input_components=3;
1797 jpeg_info.data_precision=8;
1798 jpeg_info.in_color_space=JCS_RGB;
1799 switch (image->colorspace)
1800 {
1801 case CMYKColorspace:
1802 {
1803 jpeg_info.input_components=4;
1804 jpeg_info.in_color_space=JCS_CMYK;
1805 break;
1806 }
1807 case YCbCrColorspace:
1808 case Rec601YCbCrColorspace:
1809 case Rec709YCbCrColorspace:
1810 {
1811 jpeg_info.in_color_space=JCS_YCbCr;
1812 break;
1813 }
1814 case GRAYColorspace:
1815 case Rec601LumaColorspace:
1816 case Rec709LumaColorspace:
1817 {
1818 jpeg_info.input_components=1;
1819 jpeg_info.in_color_space=JCS_GRAYSCALE;
1820 break;
1821 }
1822 default:
cristy9f396782009-12-21 01:37:45 +00001823 {
cristy510d06a2011-07-06 23:43:54 +00001824 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy9f396782009-12-21 01:37:45 +00001825 (void) TransformImageColorspace(image,RGBColorspace);
cristy3ed852e2009-09-05 21:47:34 +00001826 break;
cristy9f396782009-12-21 01:37:45 +00001827 }
cristy3ed852e2009-09-05 21:47:34 +00001828 }
1829 if ((image_info->type != TrueColorType) &&
cristy4c08aed2011-07-01 19:47:50 +00001830 (IsImageGray(image,&image->exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001831 {
1832 jpeg_info.input_components=1;
1833 jpeg_info.in_color_space=JCS_GRAYSCALE;
1834 }
1835 jpeg_set_defaults(&jpeg_info);
1836 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1837 jpeg_info.data_precision=8;
1838 else
1839 if (sizeof(JSAMPLE) > 1)
1840 jpeg_info.data_precision=12;
1841 jpeg_info.density_unit=(UINT8) 1;
1842 if (image->debug != MagickFalse)
1843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001844 "Image resolution: %.20g,%.20g",floor(image->x_resolution+0.5),
1845 floor(image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001846 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1847 {
1848 /*
1849 Set image resolution.
1850 */
1851 jpeg_info.write_JFIF_header=MagickTrue;
cristyd999ef62010-05-08 19:54:26 +00001852 jpeg_info.X_density=(UINT16) floor(image->x_resolution+0.5);
1853 jpeg_info.Y_density=(UINT16) floor(image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001854 if (image->units == PixelsPerInchResolution)
1855 jpeg_info.density_unit=(UINT8) 1;
1856 if (image->units == PixelsPerCentimeterResolution)
1857 jpeg_info.density_unit=(UINT8) 2;
1858 }
1859 option=GetImageOption(image_info,"jpeg:dct-method");
1860 if (option != (const char *) NULL)
1861 switch (*option)
1862 {
1863 case 'D':
1864 case 'd':
1865 {
1866 if (LocaleCompare(option,"default") == 0)
1867 jpeg_info.dct_method=JDCT_DEFAULT;
1868 break;
1869 }
1870 case 'F':
1871 case 'f':
1872 {
1873 if (LocaleCompare(option,"fastest") == 0)
1874 jpeg_info.dct_method=JDCT_FASTEST;
1875 if (LocaleCompare(option,"float") == 0)
1876 jpeg_info.dct_method=JDCT_FLOAT;
1877 break;
1878 }
1879 case 'I':
1880 case 'i':
1881 {
1882 if (LocaleCompare(option,"ifast") == 0)
1883 jpeg_info.dct_method=JDCT_IFAST;
1884 if (LocaleCompare(option,"islow") == 0)
1885 jpeg_info.dct_method=JDCT_ISLOW;
1886 break;
1887 }
1888 }
1889 option=GetImageOption(image_info,"jpeg:optimize-coding");
1890 if (option != (const char *) NULL)
1891 {
1892 jpeg_info.optimize_coding=MagickFalse;
1893 if (IsMagickTrue(option) != MagickFalse)
1894 jpeg_info.optimize_coding=MagickTrue;
1895 }
1896 else
1897 {
1898 MagickSizeType
1899 length;
1900
1901 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1902 image->rows*sizeof(JSAMPLE);
1903 if (length == (MagickSizeType) ((size_t) length))
1904 {
1905 /*
1906 Perform optimization only if available memory resources permit it.
1907 */
1908 status=AcquireMagickResource(MemoryResource,length);
1909 if (status != MagickFalse)
1910 jpeg_info.optimize_coding=MagickTrue;
1911 RelinquishMagickResource(MemoryResource,length);
1912 }
1913 }
1914#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1915 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1916 (image_info->interlace != NoInterlace))
1917 {
1918 if (image->debug != MagickFalse)
1919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1920 "Interlace: progressive");
1921 jpeg_simple_progression(&jpeg_info);
1922 }
1923 else
1924 if (image->debug != MagickFalse)
1925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1926 "Interlace: non-progressive");
1927#else
1928 if (image->debug != MagickFalse)
1929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1930 "Interlace: nonprogressive");
1931#endif
cristy0adb4f92009-11-28 18:08:51 +00001932 option=GetImageOption(image_info,"jpeg:extent");
1933 if (option != (const char *) NULL)
1934 {
1935 Image
1936 *jpeg_image;
1937
1938 ImageInfo
1939 *jpeg_info;
1940
1941 jpeg_info=CloneImageInfo(image_info);
1942 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1943 if (jpeg_image != (Image *) NULL)
1944 {
1945 MagickSizeType
1946 extent;
1947
1948 size_t
cristy87e73ab2010-02-12 01:59:12 +00001949 maximum,
1950 minimum;
cristy0adb4f92009-11-28 18:08:51 +00001951
1952 /*
1953 Search for compression quality that does not exceed image extent.
1954 */
1955 jpeg_info->quality=0;
cristyf2f27272009-12-17 14:48:46 +00001956 extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00001957 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1958 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00001959 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00001960 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00001961 {
cristy87e73ab2010-02-12 01:59:12 +00001962 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy0adb4f92009-11-28 18:08:51 +00001963 status=WriteJPEGImage(jpeg_info,jpeg_image);
cristyc8d69b12010-02-13 19:06:26 +00001964 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00001965 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00001966 else
cristy87e73ab2010-02-12 01:59:12 +00001967 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00001968 }
1969 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00001970 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00001971 jpeg_image=DestroyImage(jpeg_image);
1972 }
1973 jpeg_info=DestroyImageInfo(jpeg_info);
1974 }
cristy3ed852e2009-09-05 21:47:34 +00001975 if ((image_info->compression != LosslessJPEGCompression) &&
1976 (image->quality <= 100))
1977 {
1978 if (image->quality == UndefinedCompressionQuality)
1979 jpeg_set_quality(&jpeg_info,92,MagickTrue);
1980 else
1981 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
1982 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
1984 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00001985 }
1986 else
1987 {
1988#if !defined(C_LOSSLESS_SUPPORTED)
1989 jpeg_set_quality(&jpeg_info,100,MagickTrue);
1990 if (image->debug != MagickFalse)
1991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
1992#else
1993 if (image->quality < 100)
1994 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1995 CoderWarning,"LosslessToLossyJPEGConversion",image->filename);
1996 else
1997 {
1998 int
1999 point_transform,
2000 predictor;
2001
2002 predictor=image->quality/100; /* range 1-7 */
2003 point_transform=image->quality % 20; /* range 0-15 */
2004 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2005 if (image->debug != MagickFalse)
2006 {
2007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2008 "Compression: lossless");
2009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2010 "Predictor: %d",predictor);
2011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2012 "Point Transform: %d",point_transform);
2013 }
2014 }
2015#endif
2016 }
2017 sampling_factor=(const char *) NULL;
2018 value=GetImageProperty(image,"jpeg:sampling-factor");
2019 if (value != (char *) NULL)
2020 {
2021 sampling_factor=value;
2022 if (image->debug != MagickFalse)
2023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2024 " Input sampling-factors=%s",sampling_factor);
2025 }
2026 if (image_info->sampling_factor != (char *) NULL)
2027 sampling_factor=image_info->sampling_factor;
2028 if (sampling_factor == (const char *) NULL)
2029 {
2030 if (image->quality >= 90)
2031 for (i=0; i < MAX_COMPONENTS; i++)
2032 {
2033 jpeg_info.comp_info[i].h_samp_factor=1;
2034 jpeg_info.comp_info[i].v_samp_factor=1;
2035 }
2036 }
2037 else
2038 {
2039 char
2040 **factors;
2041
2042 GeometryInfo
2043 geometry_info;
2044
2045 MagickStatusType
2046 flags;
2047
2048 /*
2049 Set sampling factor.
2050 */
2051 i=0;
2052 factors=SamplingFactorToList(sampling_factor);
2053 if (factors != (char **) NULL)
2054 {
2055 for (i=0; i < MAX_COMPONENTS; i++)
2056 {
2057 if (factors[i] == (char *) NULL)
2058 break;
2059 flags=ParseGeometry(factors[i],&geometry_info);
2060 if ((flags & SigmaValue) == 0)
2061 geometry_info.sigma=geometry_info.rho;
2062 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2063 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2064 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2065 }
2066 factors=(char **) RelinquishMagickMemory(factors);
2067 }
2068 for ( ; i < MAX_COMPONENTS; i++)
2069 {
2070 jpeg_info.comp_info[i].h_samp_factor=1;
2071 jpeg_info.comp_info[i].v_samp_factor=1;
2072 }
2073 }
2074 if (jpeg_info.input_components == 1)
2075 for (i=0; i < MAX_COMPONENTS; i++)
2076 {
2077 jpeg_info.comp_info[i].h_samp_factor=1;
2078 jpeg_info.comp_info[i].v_samp_factor=1;
2079 }
2080 jpeg_start_compress(&jpeg_info,MagickTrue);
2081 if (image->debug != MagickFalse)
2082 {
2083 if (image->storage_class == PseudoClass)
2084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2085 "Storage class: PseudoClass");
2086 else
2087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2088 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2090 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002091 if (image->colors != 0)
2092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002093 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002094 else
2095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2096 "Number of colors: unspecified");
2097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2098 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2099 switch (image->colorspace)
2100 {
2101 case CMYKColorspace:
2102 {
2103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2104 "Storage class: DirectClass");
2105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2106 "Colorspace: CMYK");
2107 break;
2108 }
2109 case YCbCrColorspace:
2110 case Rec601YCbCrColorspace:
2111 case Rec709YCbCrColorspace:
2112 {
2113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2114 "Colorspace: YCbCr");
2115 break;
2116 }
2117 default:
2118 break;
2119 }
2120 switch (image->colorspace)
2121 {
2122 case CMYKColorspace:
2123 {
2124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2125 "Colorspace: CMYK");
2126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2127 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2128 jpeg_info.comp_info[0].h_samp_factor,
2129 jpeg_info.comp_info[0].v_samp_factor,
2130 jpeg_info.comp_info[1].h_samp_factor,
2131 jpeg_info.comp_info[1].v_samp_factor,
2132 jpeg_info.comp_info[2].h_samp_factor,
2133 jpeg_info.comp_info[2].v_samp_factor,
2134 jpeg_info.comp_info[3].h_samp_factor,
2135 jpeg_info.comp_info[3].v_samp_factor);
2136 break;
2137 }
2138 case GRAYColorspace:
2139 case Rec601LumaColorspace:
2140 case Rec709LumaColorspace:
2141 {
2142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2143 "Colorspace: GRAY");
2144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2145 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2146 jpeg_info.comp_info[0].v_samp_factor);
2147 break;
2148 }
2149 case RGBColorspace:
2150 {
2151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2152 "Image colorspace is RGB");
2153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2154 "Sampling factors: %dx%d,%dx%d,%dx%d",
2155 jpeg_info.comp_info[0].h_samp_factor,
2156 jpeg_info.comp_info[0].v_samp_factor,
2157 jpeg_info.comp_info[1].h_samp_factor,
2158 jpeg_info.comp_info[1].v_samp_factor,
2159 jpeg_info.comp_info[2].h_samp_factor,
2160 jpeg_info.comp_info[2].v_samp_factor);
2161 break;
2162 }
2163 case YCbCrColorspace:
2164 case Rec601YCbCrColorspace:
2165 case Rec709YCbCrColorspace:
2166 {
2167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2168 "Colorspace: YCbCr");
2169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2170 "Sampling factors: %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 break;
2178 }
2179 default:
2180 {
2181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2182 image->colorspace);
2183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2184 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2185 jpeg_info.comp_info[0].h_samp_factor,
2186 jpeg_info.comp_info[0].v_samp_factor,
2187 jpeg_info.comp_info[1].h_samp_factor,
2188 jpeg_info.comp_info[1].v_samp_factor,
2189 jpeg_info.comp_info[2].h_samp_factor,
2190 jpeg_info.comp_info[2].v_samp_factor,
2191 jpeg_info.comp_info[3].h_samp_factor,
2192 jpeg_info.comp_info[3].v_samp_factor);
2193 break;
2194 }
2195 }
2196 }
2197 /*
2198 Write JPEG profiles.
2199 */
2200 value=GetImageProperty(image,"comment");
2201 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002202 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002203 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2204 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2205 if (image->profiles != (void *) NULL)
2206 WriteProfile(&jpeg_info,image);
2207 /*
2208 Convert MIFF to JPEG raster pixels.
2209 */
2210 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2211 jpeg_info.input_components*sizeof(*jpeg_pixels));
2212 if (jpeg_pixels == (JSAMPLE *) NULL)
2213 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2214 if (setjmp(error_manager.error_recovery) != 0)
2215 {
2216 jpeg_destroy_compress(&jpeg_info);
2217 if (jpeg_pixels != (unsigned char *) NULL)
2218 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2219 (void) CloseBlob(image);
2220 return(MagickFalse);
2221 }
2222 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002223 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002224 {
cristy3ed852e2009-09-05 21:47:34 +00002225 if ((jpeg_info.in_color_space == JCS_RGB) ||
2226 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002227 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002228 {
cristy4c08aed2011-07-01 19:47:50 +00002229 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002230 *p;
2231
cristybb503372010-05-27 20:51:26 +00002232 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002233 x;
2234
2235 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002236 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002237 break;
2238 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002239 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002240 {
cristy4c08aed2011-07-01 19:47:50 +00002241 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2242 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2243 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002244 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002245 }
2246 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002247 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2248 image->rows);
2249 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002250 break;
2251 }
2252 else
cristye90d7402010-03-14 18:21:29 +00002253 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002254 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002255 {
cristy4c08aed2011-07-01 19:47:50 +00002256 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002257 *p;
2258
cristybb503372010-05-27 20:51:26 +00002259 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002260 x;
2261
2262 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002263 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002264 break;
2265 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002266 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002267 {
cristy4c08aed2011-07-01 19:47:50 +00002268 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristyed231572011-07-14 02:18:59 +00002269 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002270 }
2271 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002272 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2273 image->rows);
2274 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002275 break;
2276 }
2277 else
cristybb503372010-05-27 20:51:26 +00002278 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002279 {
cristy4c08aed2011-07-01 19:47:50 +00002280 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002281 *p;
2282
cristybb503372010-05-27 20:51:26 +00002283 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002284 x;
2285
2286 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002287 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002288 break;
2289 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002290 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002291 {
2292 /*
2293 Convert DirectClass packets to contiguous CMYK scanlines.
2294 */
2295 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002296 GetPixelRed(image,p))));
cristye90d7402010-03-14 18:21:29 +00002297 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002298 GetPixelGreen(image,p))));
cristye90d7402010-03-14 18:21:29 +00002299 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002300 GetPixelBlue(image,p))));
cristye90d7402010-03-14 18:21:29 +00002301 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002302 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002303 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002304 }
2305 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002306 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2307 image->rows);
2308 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002309 break;
2310 }
2311 }
2312 else
2313 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002314 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002315 {
cristy4c08aed2011-07-01 19:47:50 +00002316 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002317 *p;
2318
cristybb503372010-05-27 20:51:26 +00002319 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002320 x;
2321
2322 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002323 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002324 break;
2325 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002326 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002327 {
cristy4c08aed2011-07-01 19:47:50 +00002328 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >>
cristye90d7402010-03-14 18:21:29 +00002329 4);
cristyed231572011-07-14 02:18:59 +00002330 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002331 }
2332 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002333 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2334 image->rows);
2335 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002336 break;
2337 }
2338 else
2339 if ((jpeg_info.in_color_space == JCS_RGB) ||
2340 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002341 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002342 {
cristy4c08aed2011-07-01 19:47:50 +00002343 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002344 *p;
2345
cristybb503372010-05-27 20:51:26 +00002346 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002347 x;
2348
2349 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002350 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002351 break;
2352 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002353 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002354 {
cristy4c08aed2011-07-01 19:47:50 +00002355 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2356 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2357 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002358 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002359 }
2360 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002361 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002362 image->rows);
cristye90d7402010-03-14 18:21:29 +00002363 if (status == MagickFalse)
2364 break;
2365 }
2366 else
cristybb503372010-05-27 20:51:26 +00002367 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002368 {
cristy4c08aed2011-07-01 19:47:50 +00002369 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002370 *p;
2371
cristybb503372010-05-27 20:51:26 +00002372 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002373 x;
2374
2375 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002376 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002377 break;
2378 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002379 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002380 {
2381 /*
2382 Convert DirectClass packets to contiguous CMYK scanlines.
2383 */
cristye90d7402010-03-14 18:21:29 +00002384 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002385 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002386 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002387 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002388 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002389 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002390 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002391 GetPixelBlack(image,p)) >> 4));
cristyed231572011-07-14 02:18:59 +00002392 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002393 }
2394 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002395 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002396 image->rows);
cristye90d7402010-03-14 18:21:29 +00002397 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002398 break;
2399 }
cristybb503372010-05-27 20:51:26 +00002400 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002401 jpeg_finish_compress(&jpeg_info);
2402 /*
2403 Relinquish resources.
2404 */
2405 jpeg_destroy_compress(&jpeg_info);
2406 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2407 (void) CloseBlob(image);
2408 return(MagickTrue);
2409}
2410#endif