blob: 14e079a2f43e755f9959f8403775b31d640e9199 [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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"
cristye40005d2012-03-23 12:18:45 +000080#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000081#include "MagickCore/utility.h"
cristy1b58f252012-03-01 01:41:41 +000082#include "MagickCore/xml-tree.h"
83#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000084#include <setjmp.h>
85#if defined(MAGICKCORE_JPEG_DELEGATE)
86#define JPEG_INTERNAL_OPTIONS
87#if defined(__MINGW32__)
88# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
cristye7e40552010-04-24 21:34:22 +000089typedef unsigned char boolean;
cristyc6da28e2011-04-28 01:41:35 +000090#define HAVE_BOOLEAN
cristy3ed852e2009-09-05 21:47:34 +000091#endif
92#undef HAVE_STDLIB_H
93#include "jpeglib.h"
94#include "jerror.h"
95#endif
96
97/*
98 Define declarations.
99*/
100#define ICC_MARKER (JPEG_APP0+2)
101#define ICC_PROFILE "ICC_PROFILE"
102#define IPTC_MARKER (JPEG_APP0+13)
103#define XML_MARKER (JPEG_APP0+1)
cristyeb9759e2012-06-07 23:06:29 +0000104#define MaxBufferExtent 16384
cristy3ed852e2009-09-05 21:47:34 +0000105
106/*
107 Typedef declarations.
108*/
109#if defined(MAGICKCORE_JPEG_DELEGATE)
110typedef struct _DestinationManager
111{
112 struct jpeg_destination_mgr
113 manager;
114
115 Image
116 *image;
117
118 JOCTET
119 *buffer;
120} DestinationManager;
121
122typedef struct _ErrorManager
123{
cristy018f07f2011-09-04 21:15:19 +0000124 ExceptionInfo
125 *exception;
126
cristy3ed852e2009-09-05 21:47:34 +0000127 Image
128 *image;
129
cristyd28b1dd2011-05-14 20:30:38 +0000130 MagickBooleanType
131 finished;
132
cristy3ed852e2009-09-05 21:47:34 +0000133 jmp_buf
134 error_recovery;
135} ErrorManager;
136
137typedef struct _SourceManager
138{
139 struct jpeg_source_mgr
140 manager;
141
142 Image
143 *image;
144
145 JOCTET
146 *buffer;
147
148 boolean
149 start_of_blob;
150} SourceManager;
151#endif
cristy1b58f252012-03-01 01:41:41 +0000152
153typedef struct _QuantizationTable
154{
155 char
156 *slot,
157 *description;
158
159 size_t
160 width,
161 height;
162
cristy043f3f32012-03-02 17:37:28 +0000163 double
164 divisor;
165
cristy1b58f252012-03-01 01:41:41 +0000166 unsigned int
cristy1b58f252012-03-01 01:41:41 +0000167 *levels;
168} QuantizationTable;
cristy3ed852e2009-09-05 21:47:34 +0000169
170/*
171 Forward declarations.
172*/
173#if defined(MAGICKCORE_JPEG_DELEGATE)
174static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000175 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000176#endif
177
178/*
179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180% %
181% %
182% %
183% I s J P E G %
184% %
185% %
186% %
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188%
189% IsJPEG() returns MagickTrue if the image format type, identified by the
190% magick string, is JPEG.
191%
192% The format of the IsJPEG method is:
193%
194% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
195%
196% A description of each parameter follows:
197%
198% o magick: compare image format pattern against these bytes.
199%
200% o length: Specifies the length of the magick string.
201%
202*/
203static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
204{
205 if (length < 3)
206 return(MagickFalse);
207 if (memcmp(magick,"\377\330\377",3) == 0)
208 return(MagickTrue);
209 return(MagickFalse);
210}
211
212#if defined(MAGICKCORE_JPEG_DELEGATE)
213/*
214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215% %
216% %
217% %
218% R e a d J P E G I m a g e %
219% %
220% %
221% %
222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223%
224% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
225% the memory necessary for the new Image structure and returns a pointer to
226% the new image.
227%
228% The format of the ReadJPEGImage method is:
229%
230% Image *ReadJPEGImage(const ImageInfo *image_info,
231% ExceptionInfo *exception)
232%
233% A description of each parameter follows:
234%
235% o image_info: the image info.
236%
237% o exception: return any errors or warnings in this structure.
238%
239*/
240
cristy3ed852e2009-09-05 21:47:34 +0000241static boolean FillInputBuffer(j_decompress_ptr cinfo)
242{
243 SourceManager
244 *source;
245
246 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000247 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
248 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000249 if (source->manager.bytes_in_buffer == 0)
250 {
cristy453b8202012-06-07 22:57:52 +0000251 if (source->start_of_blob != FALSE)
cristy3ed852e2009-09-05 21:47:34 +0000252 ERREXIT(cinfo,JERR_INPUT_EMPTY);
253 WARNMS(cinfo,JWRN_JPEG_EOF);
254 source->buffer[0]=(JOCTET) 0xff;
255 source->buffer[1]=(JOCTET) JPEG_EOI;
256 source->manager.bytes_in_buffer=2;
257 }
258 source->manager.next_input_byte=source->buffer;
259 source->start_of_blob=FALSE;
260 return(TRUE);
261}
262
263static int GetCharacter(j_decompress_ptr jpeg_info)
264{
265 if (jpeg_info->src->bytes_in_buffer == 0)
266 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
267 jpeg_info->src->bytes_in_buffer--;
268 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
269}
270
271static void InitializeSource(j_decompress_ptr cinfo)
272{
273 SourceManager
274 *source;
275
276 source=(SourceManager *) cinfo->src;
277 source->start_of_blob=TRUE;
278}
279
cristye8dd1302009-11-11 02:45:03 +0000280static MagickBooleanType IsITUFaxImage(const Image *image)
281{
282 const StringInfo
283 *profile;
284
285 const unsigned char
286 *datum;
287
cristyace6aa42009-11-11 03:17:33 +0000288 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000289 if (profile == (const StringInfo *) NULL)
290 return(MagickFalse);
291 if (GetStringInfoLength(profile) < 5)
292 return(MagickFalse);
293 datum=GetStringInfoDatum(profile);
294 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
295 (datum[3] == 0x41) && (datum[4] == 0x58))
296 return(MagickTrue);
297 return(MagickFalse);
298}
299
cristy437c3932011-10-12 18:03:46 +0000300static void JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000301{
cristyd28b1dd2011-05-14 20:30:38 +0000302 char
303 message[JMSG_LENGTH_MAX];
304
cristy3ed852e2009-09-05 21:47:34 +0000305 ErrorManager
306 *error_manager;
307
cristyc82a27b2011-10-21 01:07:16 +0000308 ExceptionInfo
309 *exception;
310
cristyd28b1dd2011-05-14 20:30:38 +0000311 Image
312 *image;
313
314 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000315 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000316 image=error_manager->image;
cristyc82a27b2011-10-21 01:07:16 +0000317 exception=error_manager->exception;
cristy86f33542011-05-21 22:58:33 +0000318 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000319 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
321 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000322 if (error_manager->finished != MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000323 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
324 (char *) message,"`%s'",image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000325 else
cristyc82a27b2011-10-21 01:07:16 +0000326 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
327 (char *) message,"`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000328 longjmp(error_manager->error_recovery,1);
329}
330
cristyd28b1dd2011-05-14 20:30:38 +0000331static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
332{
cristyf162e012012-03-28 12:54:33 +0000333#define JPEGExcessiveWarnings 1000
334
cristyd28b1dd2011-05-14 20:30:38 +0000335 char
336 message[JMSG_LENGTH_MAX];
337
338 ErrorManager
339 *error_manager;
340
cristy018f07f2011-09-04 21:15:19 +0000341 ExceptionInfo
342 *exception;
343
cristyd28b1dd2011-05-14 20:30:38 +0000344 Image
345 *image;
346
347 *message='\0';
348 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000349 exception=error_manager->exception;
cristyd28b1dd2011-05-14 20:30:38 +0000350 image=error_manager->image;
351 if (level < 0)
352 {
353 /*
354 Process warning message.
355 */
356 (jpeg_info->err->format_message)(jpeg_info,message);
cristyf162e012012-03-28 12:54:33 +0000357 if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
358 JPEGErrorHandler(jpeg_info);
cristy8043fb32012-06-28 16:14:12 +0000359 ThrowBinaryException(CorruptImageWarning,(char *) message,
360 image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000361 }
362 else
363 if ((image->debug != MagickFalse) &&
364 (level >= jpeg_info->err->trace_level))
365 {
366 /*
367 Process trace message.
368 */
369 (jpeg_info->err->format_message)(jpeg_info,message);
370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
371 "[%s] JPEG Trace: \"%s\"",image->filename,message);
372 }
373 return(MagickTrue);
374}
375
cristy3ed852e2009-09-05 21:47:34 +0000376static boolean ReadComment(j_decompress_ptr jpeg_info)
377{
378 char
379 *comment;
380
381 ErrorManager
382 *error_manager;
383
cristy018f07f2011-09-04 21:15:19 +0000384 ExceptionInfo
385 *exception;
386
cristy3ed852e2009-09-05 21:47:34 +0000387 Image
388 *image;
389
390 register char
391 *p;
392
cristybb503372010-05-27 20:51:26 +0000393 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000394 i;
395
396 size_t
397 length;
398
399 /*
400 Determine length of comment.
401 */
402 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000403 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000404 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000405 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000406 length+=GetCharacter(jpeg_info);
407 length-=2;
408 if (length <= 0)
409 return(MagickTrue);
410 comment=(char *) NULL;
cristy37e0b382011-06-07 13:31:21 +0000411 if (~length >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000412 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
413 sizeof(*comment));
414 if (comment == (char *) NULL)
415 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
416 image->filename);
417 /*
418 Read comment.
419 */
cristybb503372010-05-27 20:51:26 +0000420 i=(ssize_t) length-1;
cristy3ed852e2009-09-05 21:47:34 +0000421 for (p=comment; i-- >= 0; p++)
422 *p=(char) GetCharacter(jpeg_info);
423 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000424 (void) SetImageProperty(image,"comment",comment,exception);
cristy3ed852e2009-09-05 21:47:34 +0000425 comment=DestroyString(comment);
426 return(MagickTrue);
427}
428
429static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
430{
431 char
432 magick[12];
433
434 ErrorManager
435 *error_manager;
436
cristy018f07f2011-09-04 21:15:19 +0000437 ExceptionInfo
438 *exception;
439
cristy3ed852e2009-09-05 21:47:34 +0000440 Image
441 *image;
442
443 MagickBooleanType
444 status;
445
cristybb503372010-05-27 20:51:26 +0000446 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000447 i;
448
449 register unsigned char
450 *p;
451
452 size_t
453 length;
454
455 StringInfo
456 *icc_profile,
457 *profile;
458
459 /*
460 Read color profile.
461 */
cristybb503372010-05-27 20:51:26 +0000462 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000463 length+=(size_t) GetCharacter(jpeg_info);
464 length-=2;
465 if (length <= 14)
466 {
467 while (length-- > 0)
468 (void) GetCharacter(jpeg_info);
469 return(MagickTrue);
470 }
471 for (i=0; i < 12; i++)
472 magick[i]=(char) GetCharacter(jpeg_info);
473 if (LocaleCompare(magick,ICC_PROFILE) != 0)
474 {
475 /*
476 Not a ICC profile, return.
477 */
cristybb503372010-05-27 20:51:26 +0000478 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000479 (void) GetCharacter(jpeg_info);
480 return(MagickTrue);
481 }
482 (void) GetCharacter(jpeg_info); /* id */
483 (void) GetCharacter(jpeg_info); /* markers */
484 length-=14;
485 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000486 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000487 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000488 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000489 if (profile == (StringInfo *) NULL)
490 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
491 image->filename);
492 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000493 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000494 *p++=(unsigned char) GetCharacter(jpeg_info);
495 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
496 if (icc_profile != (StringInfo *) NULL)
497 {
498 ConcatenateStringInfo(icc_profile,profile);
499 profile=DestroyStringInfo(profile);
500 }
501 else
502 {
cristyd15e6592011-10-15 00:13:06 +0000503 status=SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000504 profile=DestroyStringInfo(profile);
505 if (status == MagickFalse)
506 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
507 image->filename);
508 }
509 if (image->debug != MagickFalse)
510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000511 "Profile: ICC, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000512 return(MagickTrue);
513}
514
515static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
516{
517 char
518 magick[MaxTextExtent];
519
520 ErrorManager
521 *error_manager;
522
cristy018f07f2011-09-04 21:15:19 +0000523 ExceptionInfo
524 *exception;
525
cristy3ed852e2009-09-05 21:47:34 +0000526 Image
527 *image;
528
529 MagickBooleanType
530 status;
531
cristybb503372010-05-27 20:51:26 +0000532 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000533 i;
534
535 register unsigned char
536 *p;
537
538 size_t
539 length;
540
541 StringInfo
542 *iptc_profile,
543 *profile;
544
545 /*
546 Determine length of binary data stored here.
547 */
cristybb503372010-05-27 20:51:26 +0000548 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000549 length+=(size_t) GetCharacter(jpeg_info);
550 length-=2;
551 if (length <= 14)
552 {
553 while (length-- > 0)
554 (void) GetCharacter(jpeg_info);
555 return(MagickTrue);
556 }
557 /*
558 Validate that this was written as a Photoshop resource format slug.
559 */
560 for (i=0; i < 10; i++)
561 magick[i]=(char) GetCharacter(jpeg_info);
562 magick[10]='\0';
563 if (length <= 10)
564 return(MagickTrue);
565 length-=10;
566 if (LocaleCompare(magick,"Photoshop ") != 0)
567 {
568 /*
569 Not a IPTC profile, return.
570 */
cristybb503372010-05-27 20:51:26 +0000571 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000572 (void) GetCharacter(jpeg_info);
573 return(MagickTrue);
574 }
575 /*
576 Remove the version number.
577 */
578 for (i=0; i < 4; i++)
579 (void) GetCharacter(jpeg_info);
580 if (length <= 4)
581 return(MagickTrue);
582 length-=4;
583 if (length == 0)
584 return(MagickTrue);
585 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000586 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000587 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000588 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000589 if (profile == (StringInfo *) NULL)
590 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
591 image->filename);
592 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000593 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000594 *p++=(unsigned char) GetCharacter(jpeg_info);
595 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
596 if (iptc_profile != (StringInfo *) NULL)
597 {
598 ConcatenateStringInfo(iptc_profile,profile);
599 profile=DestroyStringInfo(profile);
600 }
601 else
602 {
cristyd15e6592011-10-15 00:13:06 +0000603 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000604 profile=DestroyStringInfo(profile);
605 if (status == MagickFalse)
606 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
607 image->filename);
608 }
609 if (image->debug != MagickFalse)
610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000611 "Profile: iptc, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000612 return(MagickTrue);
613}
614
615static boolean ReadProfile(j_decompress_ptr jpeg_info)
616{
617 char
618 name[MaxTextExtent];
619
cristye23ec9d2011-08-16 18:15:40 +0000620 const StringInfo
621 *previous_profile;
622
cristy3ed852e2009-09-05 21:47:34 +0000623 ErrorManager
624 *error_manager;
625
cristy018f07f2011-09-04 21:15:19 +0000626 ExceptionInfo
627 *exception;
628
cristy3ed852e2009-09-05 21:47:34 +0000629 Image
630 *image;
631
632 int
633 marker;
634
635 MagickBooleanType
636 status;
637
cristybb503372010-05-27 20:51:26 +0000638 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000639 i;
640
641 register unsigned char
642 *p;
643
644 size_t
645 length;
646
647 StringInfo
648 *profile;
649
650 /*
651 Read generic profile.
652 */
cristybb503372010-05-27 20:51:26 +0000653 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000654 length+=(size_t) GetCharacter(jpeg_info);
655 if (length <= 2)
656 return(MagickTrue);
657 length-=2;
658 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000659 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000660 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000661 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000662 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000663 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000664 if (profile == (StringInfo *) NULL)
665 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
666 image->filename);
667 p=GetStringInfoDatum(profile);
cristy08f255a2011-08-17 01:23:06 +0000668 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000669 *p++=(unsigned char) GetCharacter(jpeg_info);
670 if (marker == 1)
671 {
672 p=GetStringInfoDatum(profile);
673 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
674 (void) CopyMagickString(name,"exif",MaxTextExtent);
675 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
676 {
cristybb503372010-05-27 20:51:26 +0000677 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000678 j;
679
680 /*
681 Extract namespace from XMP profile.
682 */
683 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000684 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000685 {
686 if (*p == '\0')
687 break;
688 p++;
689 }
cristybb503372010-05-27 20:51:26 +0000690 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000691 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
692 (void) CopyMagickString(name,"xmp",MaxTextExtent);
693 }
694 }
cristye23ec9d2011-08-16 18:15:40 +0000695 previous_profile=GetImageProfile(image,name);
696 if (previous_profile != (const StringInfo *) NULL)
cristy08f255a2011-08-17 01:23:06 +0000697 {
cristy1b58f252012-03-01 01:41:41 +0000698 size_t
cristy05c0c9a2011-09-05 23:16:13 +0000699 length;
700
701 length=GetStringInfoLength(profile);
cristy08f255a2011-08-17 01:23:06 +0000702 SetStringInfoLength(profile,GetStringInfoLength(profile)+
703 GetStringInfoLength(previous_profile));
cristy83ab3d82011-09-06 12:05:51 +0000704 (void) memmove(GetStringInfoDatum(profile)+
705 GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
706 length);
cristy08f255a2011-08-17 01:23:06 +0000707 (void) memcpy(GetStringInfoDatum(profile),
708 GetStringInfoDatum(previous_profile),
709 GetStringInfoLength(previous_profile));
710 }
cristyd15e6592011-10-15 00:13:06 +0000711 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000712 profile=DestroyStringInfo(profile);
713 if (status == MagickFalse)
714 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
715 image->filename);
716 if (image->debug != MagickFalse)
717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000718 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000719 return(MagickTrue);
720}
721
cristyf2faecf2010-05-28 19:19:36 +0000722static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000723{
724 SourceManager
725 *source;
726
727 if (number_bytes <= 0)
728 return;
729 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000730 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000731 {
cristyf2faecf2010-05-28 19:19:36 +0000732 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000733 (void) FillInputBuffer(cinfo);
734 }
cristy4cb162a2010-05-30 03:04:47 +0000735 source->manager.next_input_byte+=number_bytes;
736 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000737}
738
739static void TerminateSource(j_decompress_ptr cinfo)
740{
cristydf0d90e2011-12-12 01:03:55 +0000741 (void) cinfo;
cristy3ed852e2009-09-05 21:47:34 +0000742}
743
744static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
745{
746 SourceManager
747 *source;
748
749 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
750 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
751 source=(SourceManager *) cinfo->src;
752 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
753 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
754 source=(SourceManager *) cinfo->src;
755 source->manager.init_source=InitializeSource;
756 source->manager.fill_input_buffer=FillInputBuffer;
757 source->manager.skip_input_data=SkipInputData;
758 source->manager.resync_to_restart=jpeg_resync_to_restart;
759 source->manager.term_source=TerminateSource;
760 source->manager.bytes_in_buffer=0;
761 source->manager.next_input_byte=NULL;
762 source->image=image;
763}
764
765static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
766 Image *image)
767{
768 image->quality=UndefinedCompressionQuality;
769#if defined(D_PROGRESSIVE_SUPPORTED)
770 if (image->compression == LosslessJPEGCompression)
771 {
772 image->quality=100;
773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
774 "Quality: 100 (lossless)");
775 }
776 else
777#endif
778 {
cristybb503372010-05-27 20:51:26 +0000779 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000780 j,
781 qvalue,
782 sum;
783
cristybb503372010-05-27 20:51:26 +0000784 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000785 i;
786
787 /*
788 Determine the JPEG compression quality from the quantization tables.
789 */
790 sum=0;
791 for (i=0; i < NUM_QUANT_TBLS; i++)
792 {
793 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
794 for (j=0; j < DCTSIZE2; j++)
795 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
796 }
797 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
798 (jpeg_info->quant_tbl_ptrs[1] != NULL))
799 {
cristybb503372010-05-27 20:51:26 +0000800 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000801 hash[101] =
802 {
803 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
804 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
805 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
806 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
807 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
808 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
809 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
810 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
811 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
812 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
813 0
814 },
815 sums[101] =
816 {
817 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
818 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
819 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
820 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
821 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
822 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
823 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
824 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
825 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
826 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
827 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
828 128, 0
829 };
830
cristybb503372010-05-27 20:51:26 +0000831 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000832 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
833 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
834 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
835 for (i=0; i < 100; i++)
836 {
837 if ((qvalue < hash[i]) && (sum < sums[i]))
838 continue;
cristyb6c017e2010-01-13 19:11:39 +0000839 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000840 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000841 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000843 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000844 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000845 break;
846 }
847 }
848 else
849 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
850 {
cristybb503372010-05-27 20:51:26 +0000851 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000852 hash[101] =
853 {
854 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
855 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
856 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
857 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
858 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
859 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
860 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
861 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
862 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
863 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
864 0
865 },
866 sums[101] =
867 {
868 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
869 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
870 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
871 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
872 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
873 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
874 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
875 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
876 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
877 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
878 667, 592, 518, 441, 369, 292, 221, 151, 86,
879 64, 0
880 };
881
cristybb503372010-05-27 20:51:26 +0000882 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000883 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
884 for (i=0; i < 100; i++)
885 {
886 if ((qvalue < hash[i]) && (sum < sums[i]))
887 continue;
cristyb6c017e2010-01-13 19:11:39 +0000888 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000889 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000890 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000892 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000893 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000894 break;
895 }
896 }
897 }
898}
899
cristyd15e6592011-10-15 00:13:06 +0000900static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000901{
902 char
903 sampling_factor[MaxTextExtent];
904
905 switch (jpeg_info->out_color_space)
906 {
907 case JCS_CMYK:
908 {
909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000910 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000911 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
912 jpeg_info->comp_info[0].v_samp_factor,
913 jpeg_info->comp_info[1].h_samp_factor,
914 jpeg_info->comp_info[1].v_samp_factor,
915 jpeg_info->comp_info[2].h_samp_factor,
916 jpeg_info->comp_info[2].v_samp_factor,
917 jpeg_info->comp_info[3].h_samp_factor,
918 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000919 break;
cristy3ed852e2009-09-05 21:47:34 +0000920 }
921 case JCS_GRAYSCALE:
922 {
923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
924 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000925 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000926 jpeg_info->comp_info[0].h_samp_factor,
927 jpeg_info->comp_info[0].v_samp_factor);
928 break;
929 }
930 case JCS_RGB:
931 {
932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000933 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000934 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
935 jpeg_info->comp_info[0].v_samp_factor,
936 jpeg_info->comp_info[1].h_samp_factor,
937 jpeg_info->comp_info[1].v_samp_factor,
938 jpeg_info->comp_info[2].h_samp_factor,
939 jpeg_info->comp_info[2].v_samp_factor);
940 break;
941 }
942 default:
943 {
944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
945 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000946 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000947 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
948 jpeg_info->comp_info[0].v_samp_factor,
949 jpeg_info->comp_info[1].h_samp_factor,
950 jpeg_info->comp_info[1].v_samp_factor,
951 jpeg_info->comp_info[2].h_samp_factor,
952 jpeg_info->comp_info[2].v_samp_factor,
953 jpeg_info->comp_info[3].h_samp_factor,
954 jpeg_info->comp_info[3].v_samp_factor);
955 break;
956 }
957 }
cristyd15e6592011-10-15 00:13:06 +0000958 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
959 exception);
cristye90d7402010-03-14 18:21:29 +0000960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
961 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000962}
963
964static Image *ReadJPEGImage(const ImageInfo *image_info,
965 ExceptionInfo *exception)
966{
967 char
968 value[MaxTextExtent];
969
cristycaec74e2009-09-14 02:20:04 +0000970 const char
cristy11151212009-09-14 13:10:15 +0000971 *option;
cristycaec74e2009-09-14 02:20:04 +0000972
cristy3ed852e2009-09-05 21:47:34 +0000973 ErrorManager
974 error_manager;
975
cristy3ed852e2009-09-05 21:47:34 +0000976 Image
977 *image;
978
cristy3ed852e2009-09-05 21:47:34 +0000979 JSAMPLE
980 *jpeg_pixels;
981
982 JSAMPROW
983 scanline[1];
984
985 MagickBooleanType
986 debug,
987 status;
988
989 MagickSizeType
990 number_pixels;
991
cristy4c08aed2011-07-01 19:47:50 +0000992 Quantum
993 index;
994
cristybb503372010-05-27 20:51:26 +0000995 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000996 i;
997
998 struct jpeg_decompress_struct
999 jpeg_info;
1000
1001 struct jpeg_error_mgr
1002 jpeg_error;
1003
1004 register JSAMPLE
1005 *p;
1006
cristybb503372010-05-27 20:51:26 +00001007 size_t
cristy3ed852e2009-09-05 21:47:34 +00001008 precision,
1009 units;
1010
cristy524222d2011-04-25 00:37:06 +00001011 ssize_t
1012 y;
1013
cristy3ed852e2009-09-05 21:47:34 +00001014 /*
1015 Open image file.
1016 */
1017 assert(image_info != (const ImageInfo *) NULL);
1018 assert(image_info->signature == MagickSignature);
1019 if (image_info->debug != MagickFalse)
1020 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1021 image_info->filename);
1022 assert(exception != (ExceptionInfo *) NULL);
1023 assert(exception->signature == MagickSignature);
1024 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +00001025 (void) debug;
cristy9950d572011-10-01 18:22:35 +00001026 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001027 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1028 if (status == MagickFalse)
1029 {
1030 image=DestroyImageList(image);
1031 return((Image *) NULL);
1032 }
1033 /*
1034 Initialize JPEG parameters.
1035 */
cristy91044972011-04-22 14:21:16 +00001036 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001037 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1038 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1039 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001040 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +00001041 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
1042 jpeg_pixels=(JSAMPLE *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001043 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001044 error_manager.image=image;
1045 if (setjmp(error_manager.error_recovery) != 0)
1046 {
1047 jpeg_destroy_decompress(&jpeg_info);
1048 (void) CloseBlob(image);
1049 number_pixels=(MagickSizeType) image->columns*image->rows;
1050 if (number_pixels != 0)
1051 return(GetFirstImageInList(image));
1052 return(DestroyImage(image));
1053 }
1054 jpeg_info.client_data=(void *) &error_manager;
1055 jpeg_create_decompress(&jpeg_info);
1056 JPEGSourceManager(&jpeg_info,image);
1057 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
1058 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
1059 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1060 for (i=1; i < 16; i++)
1061 if ((i != 2) && (i != 13) && (i != 14))
1062 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +00001063 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +00001064 if ((image_info->colorspace == YCbCrColorspace) ||
1065 (image_info->colorspace == Rec601YCbCrColorspace) ||
1066 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001067 jpeg_info.out_color_space=JCS_YCbCr;
1068 /*
1069 Set image resolution.
1070 */
1071 units=0;
1072 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1073 (jpeg_info.Y_density != 1))
1074 {
cristy2a11bef2011-10-28 18:33:11 +00001075 image->resolution.x=(double) jpeg_info.X_density;
1076 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001077 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001078 }
1079 if (units == 1)
1080 image->units=PixelsPerInchResolution;
1081 if (units == 2)
1082 image->units=PixelsPerCentimeterResolution;
1083 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +00001084 option=GetImageOption(image_info,"jpeg:size");
1085 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001086 {
1087 double
1088 scale_factor;
1089
cristycaec74e2009-09-14 02:20:04 +00001090 GeometryInfo
1091 geometry_info;
1092
cristy0adb4f92009-11-28 18:08:51 +00001093 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001094 flags;
1095
cristy3ed852e2009-09-05 21:47:34 +00001096 /*
cristycaec74e2009-09-14 02:20:04 +00001097 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001098 */
cristy11151212009-09-14 13:10:15 +00001099 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001100 if ((flags & SigmaValue) == 0)
1101 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001102 jpeg_calc_output_dimensions(&jpeg_info);
1103 image->magick_columns=jpeg_info.output_width;
1104 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001105 scale_factor=1.0;
1106 if (geometry_info.rho != 0.0)
1107 scale_factor=jpeg_info.output_width/geometry_info.rho;
1108 if ((geometry_info.sigma != 0.0) &&
1109 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1110 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001111 jpeg_info.scale_num=1U;
1112 jpeg_info.scale_denom=(unsigned int) scale_factor;
1113 jpeg_calc_output_dimensions(&jpeg_info);
1114 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1116 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001117 }
cristybb503372010-05-27 20:51:26 +00001118 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001119#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1120#if defined(D_LOSSLESS_SUPPORTED)
1121 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1122 JPEGInterlace : NoInterlace;
1123 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1124 LosslessJPEGCompression : JPEGCompression;
1125 if (jpeg_info.data_precision > 8)
1126 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1127 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1128 image->filename);
1129 if (jpeg_info.data_precision == 16)
1130 jpeg_info.data_precision=12;
1131#else
1132 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1133 NoInterlace;
1134 image->compression=JPEGCompression;
1135#endif
1136#else
1137 image->compression=JPEGCompression;
1138 image->interlace=JPEGInterlace;
1139#endif
anthony41906552011-10-07 12:15:27 +00001140 option=GetImageOption(image_info,"jpeg:colors");
1141 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001142 {
cristyb4bb39c2012-02-21 18:45:54 +00001143 /*
1144 Let the JPEG library quantize the image.
1145 */
cristy3ed852e2009-09-05 21:47:34 +00001146 jpeg_info.quantize_colors=MagickTrue;
anthony41906552011-10-07 12:15:27 +00001147 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001148 }
cristy1b8d4462009-10-27 13:46:56 +00001149 option=GetImageOption(image_info,"jpeg:block-smoothing");
anthony6f201312012-03-30 04:08:15 +00001150 jpeg_info.do_block_smoothing=IsStringTrue(option);
cristyb4bb39c2012-02-21 18:45:54 +00001151 jpeg_info.dct_method=JDCT_FLOAT;
cristy787d4352010-03-06 13:55:58 +00001152 option=GetImageOption(image_info,"jpeg:dct-method");
1153 if (option != (const char *) NULL)
1154 switch (*option)
1155 {
1156 case 'D':
1157 case 'd':
1158 {
1159 if (LocaleCompare(option,"default") == 0)
1160 jpeg_info.dct_method=JDCT_DEFAULT;
1161 break;
1162 }
1163 case 'F':
1164 case 'f':
1165 {
1166 if (LocaleCompare(option,"fastest") == 0)
1167 jpeg_info.dct_method=JDCT_FASTEST;
1168 if (LocaleCompare(option,"float") == 0)
1169 jpeg_info.dct_method=JDCT_FLOAT;
1170 break;
1171 }
1172 case 'I':
1173 case 'i':
1174 {
1175 if (LocaleCompare(option,"ifast") == 0)
1176 jpeg_info.dct_method=JDCT_IFAST;
1177 if (LocaleCompare(option,"islow") == 0)
1178 jpeg_info.dct_method=JDCT_ISLOW;
1179 break;
1180 }
1181 }
cristy1b8d4462009-10-27 13:46:56 +00001182 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
anthony6f201312012-03-30 04:08:15 +00001183 jpeg_info.do_fancy_upsampling=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00001184 (void) jpeg_start_decompress(&jpeg_info);
1185 image->columns=jpeg_info.output_width;
1186 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001187 image->depth=(size_t) jpeg_info.data_precision;
cristy93ff2cc2012-05-13 21:03:53 +00001188 switch (jpeg_info.out_color_space)
1189 {
1190 case JCS_RGB:
1191 default:
1192 {
1193 SetImageColorspace(image,sRGBColorspace,exception);
1194 break;
1195 }
1196 case JCS_GRAYSCALE:
1197 {
1198 SetImageColorspace(image,GRAYColorspace,exception);
1199 break;
1200 }
1201 case JCS_YCbCr:
1202 {
1203 SetImageColorspace(image,YCbCrColorspace,exception);
1204 break;
1205 }
1206 case JCS_CMYK:
1207 {
1208 SetImageColorspace(image,CMYKColorspace,exception);
1209 break;
1210 }
1211 }
cristy36fc5502012-05-22 11:32:23 +00001212 if (IsITUFaxImage(image) != MagickFalse)
1213 {
1214 SetImageColorspace(image,LabColorspace,exception);
1215 jpeg_info.out_color_space=JCS_YCbCr;
1216 }
anthony41906552011-10-07 12:15:27 +00001217 option=GetImageOption(image_info,"jpeg:colors");
1218 if (option != (const char *) NULL)
1219 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1220 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001221 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1222 if ((jpeg_info.output_components == 1) &&
1223 (jpeg_info.quantize_colors == MagickFalse))
1224 {
cristybb503372010-05-27 20:51:26 +00001225 size_t
cristy3ed852e2009-09-05 21:47:34 +00001226 colors;
1227
cristybb503372010-05-27 20:51:26 +00001228 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001229 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001230 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1231 }
1232 if (image->debug != MagickFalse)
1233 {
1234 if (image->interlace != NoInterlace)
1235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1236 "Interlace: progressive");
1237 else
1238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1239 "Interlace: nonprogressive");
1240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1241 (int) jpeg_info.data_precision);
1242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1243 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1244 }
1245 JPEGSetImageQuality(&jpeg_info,image);
cristyd15e6592011-10-15 00:13:06 +00001246 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristyb51dff52011-05-19 16:55:47 +00001247 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001248 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001249 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001250 if (image_info->ping != MagickFalse)
1251 {
1252 jpeg_destroy_decompress(&jpeg_info);
1253 (void) CloseBlob(image);
1254 return(GetFirstImageInList(image));
1255 }
1256 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1257 jpeg_info.output_components*sizeof(JSAMPLE));
1258 if (jpeg_pixels == (JSAMPLE *) NULL)
1259 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1260 /*
1261 Convert JPEG pixels to pixel packets.
1262 */
1263 if (setjmp(error_manager.error_recovery) != 0)
1264 {
1265 if (jpeg_pixels != (unsigned char *) NULL)
1266 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1267 jpeg_destroy_decompress(&jpeg_info);
1268 (void) CloseBlob(image);
1269 number_pixels=(MagickSizeType) image->columns*image->rows;
1270 if (number_pixels != 0)
1271 return(GetFirstImageInList(image));
1272 return(DestroyImage(image));
1273 }
1274 if (jpeg_info.quantize_colors != MagickFalse)
1275 {
cristybb503372010-05-27 20:51:26 +00001276 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001277 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001278 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001279 {
cristy1b58f252012-03-01 01:41:41 +00001280 image->colormap[i].red=(double) ScaleCharToQuantum(
1281 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001282 image->colormap[i].green=image->colormap[i].red;
1283 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001284 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001285 }
1286 else
cristybb503372010-05-27 20:51:26 +00001287 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001288 {
cristy1b58f252012-03-01 01:41:41 +00001289 image->colormap[i].red=(double) ScaleCharToQuantum(
1290 jpeg_info.colormap[0][i]);
1291 image->colormap[i].green=(double) ScaleCharToQuantum(
1292 jpeg_info.colormap[1][i]);
1293 image->colormap[i].blue=(double) ScaleCharToQuantum(
1294 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001295 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001296 }
1297 }
1298 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001299 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001300 {
cristybb503372010-05-27 20:51:26 +00001301 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001302 x;
1303
cristy4c08aed2011-07-01 19:47:50 +00001304 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001305 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001306
1307 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1308 {
1309 (void) ThrowMagickException(exception,GetMagickModule(),
1310 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1311 continue;
1312 }
1313 p=jpeg_pixels;
1314 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001315 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001316 break;
cristy3ed852e2009-09-05 21:47:34 +00001317 if (jpeg_info.data_precision > 8)
1318 {
1319 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001320 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001321 {
cristybb503372010-05-27 20:51:26 +00001322 size_t
cristy3ed852e2009-09-05 21:47:34 +00001323 pixel;
1324
1325 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001326 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001327 else
cristybb503372010-05-27 20:51:26 +00001328 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristyc82a27b2011-10-21 01:07:16 +00001329 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001330 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001331 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001332 p++;
cristyed231572011-07-14 02:18:59 +00001333 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001334 }
1335 else
1336 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001337 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001338 {
cristy4c08aed2011-07-01 19:47:50 +00001339 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1340 (GETJSAMPLE(*p++) << 4)),q);
1341 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1342 (GETJSAMPLE(*p++) << 4)),q);
1343 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1344 (GETJSAMPLE(*p++) << 4)),q);
1345 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001346 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001347 }
1348 else
cristybb503372010-05-27 20:51:26 +00001349 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001350 {
cristy4c08aed2011-07-01 19:47:50 +00001351 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1352 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1353 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1354 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1355 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1356 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1357 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1358 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1359 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001360 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001361 }
1362 }
1363 else
1364 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001365 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001366 {
cristyc82a27b2011-10-21 01:07:16 +00001367 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001368 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001369 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001370 p++;
cristyed231572011-07-14 02:18:59 +00001371 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001372 }
1373 else
1374 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001375 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001376 {
cristy4c08aed2011-07-01 19:47:50 +00001377 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1378 GETJSAMPLE(*p++)),q);
1379 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1380 GETJSAMPLE(*p++)),q);
1381 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1382 GETJSAMPLE(*p++)),q);
1383 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001384 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001385 }
1386 else
cristybb503372010-05-27 20:51:26 +00001387 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001388 {
cristy4c08aed2011-07-01 19:47:50 +00001389 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1390 (unsigned char) GETJSAMPLE(*p++)),q);
1391 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1392 (unsigned char) GETJSAMPLE(*p++)),q);
1393 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1394 (unsigned char) GETJSAMPLE(*p++)),q);
1395 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1396 (unsigned char) GETJSAMPLE(*p++)),q);
1397 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001398 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001399 }
1400 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1401 break;
cristy524222d2011-04-25 00:37:06 +00001402 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1403 image->rows);
1404 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001405 {
1406 jpeg_abort_decompress(&jpeg_info);
1407 break;
1408 }
cristy3ed852e2009-09-05 21:47:34 +00001409 }
cristyd28b1dd2011-05-14 20:30:38 +00001410 if (status != MagickFalse)
1411 {
1412 error_manager.finished=MagickTrue;
1413 if (setjmp(error_manager.error_recovery) == 0)
1414 (void) jpeg_finish_decompress(&jpeg_info);
1415 }
cristy3ed852e2009-09-05 21:47:34 +00001416 /*
1417 Free jpeg resources.
1418 */
cristy3ed852e2009-09-05 21:47:34 +00001419 jpeg_destroy_decompress(&jpeg_info);
1420 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1421 (void) CloseBlob(image);
1422 return(GetFirstImageInList(image));
1423}
1424#endif
1425
1426/*
1427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1428% %
1429% %
1430% %
1431% R e g i s t e r J P E G I m a g e %
1432% %
1433% %
1434% %
1435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1436%
1437% RegisterJPEGImage() adds properties for the JPEG image format to
1438% the list of supported formats. The properties include the image format
1439% tag, a method to read and/or write the format, whether the format
1440% supports the saving of more than one frame to the same file or blob,
1441% whether the format supports native in-memory I/O, and a brief
1442% description of the format.
1443%
1444% The format of the RegisterJPEGImage method is:
1445%
cristybb503372010-05-27 20:51:26 +00001446% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001447%
1448*/
cristybb503372010-05-27 20:51:26 +00001449ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001450{
1451 char
1452 version[MaxTextExtent];
1453
1454 MagickInfo
1455 *entry;
1456
1457 static const char
cristy7138c592009-09-08 13:58:52 +00001458 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001459
1460 *version='\0';
1461#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001462 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001463#endif
1464 entry=SetMagickInfo("JPEG");
1465 entry->thread_support=NoThreadSupport;
1466#if defined(MAGICKCORE_JPEG_DELEGATE)
1467 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1468 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1469#endif
1470 entry->magick=(IsImageFormatHandler *) IsJPEG;
1471 entry->adjoin=MagickFalse;
1472 entry->description=ConstantString(description);
1473 if (*version != '\0')
1474 entry->version=ConstantString(version);
1475 entry->module=ConstantString("JPEG");
1476 (void) RegisterMagickInfo(entry);
1477 entry=SetMagickInfo("JPG");
1478 entry->thread_support=NoThreadSupport;
1479#if defined(MAGICKCORE_JPEG_DELEGATE)
1480 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1481 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1482#endif
1483 entry->adjoin=MagickFalse;
1484 entry->description=ConstantString(description);
1485 if (*version != '\0')
1486 entry->version=ConstantString(version);
1487 entry->module=ConstantString("JPEG");
1488 (void) RegisterMagickInfo(entry);
1489 entry=SetMagickInfo("PJPEG");
1490 entry->thread_support=NoThreadSupport;
1491#if defined(MAGICKCORE_JPEG_DELEGATE)
1492 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1493 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1494#endif
1495 entry->adjoin=MagickFalse;
1496 entry->description=ConstantString(description);
1497 if (*version != '\0')
1498 entry->version=ConstantString(version);
1499 entry->module=ConstantString("JPEG");
1500 (void) RegisterMagickInfo(entry);
1501 return(MagickImageCoderSignature);
1502}
1503
1504/*
1505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1506% %
1507% %
1508% %
1509% U n r e g i s t e r J P E G I m a g e %
1510% %
1511% %
1512% %
1513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1514%
1515% UnregisterJPEGImage() removes format registrations made by the
1516% JPEG module from the list of supported formats.
1517%
1518% The format of the UnregisterJPEGImage method is:
1519%
1520% UnregisterJPEGImage(void)
1521%
1522*/
1523ModuleExport void UnregisterJPEGImage(void)
1524{
1525 (void) UnregisterMagickInfo("PJPG");
1526 (void) UnregisterMagickInfo("JPEG");
1527 (void) UnregisterMagickInfo("JPG");
1528}
1529
1530#if defined(MAGICKCORE_JPEG_DELEGATE)
1531/*
1532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1533% %
1534% %
1535% %
1536% W r i t e J P E G I m a g e %
1537% %
1538% %
1539% %
1540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1541%
1542% WriteJPEGImage() writes a JPEG image file and returns it. It
1543% allocates the memory necessary for the new Image structure and returns a
1544% pointer to the new image.
1545%
1546% The format of the WriteJPEGImage method is:
1547%
cristy91044972011-04-22 14:21:16 +00001548% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001549% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001550%
1551% A description of each parameter follows:
1552%
1553% o image_info: the image info.
1554%
1555% o jpeg_image: The image.
1556%
cristy1e178e72011-08-28 19:44:34 +00001557% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001558%
1559*/
1560
cristy1b58f252012-03-01 01:41:41 +00001561static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1562{
1563 assert(table != (QuantizationTable *) NULL);
1564 if (table->slot != (char *) NULL)
1565 table->slot=DestroyString(table->slot);
1566 if (table->description != (char *) NULL)
1567 table->description=DestroyString(table->description);
1568 if (table->levels != (unsigned int *) NULL)
1569 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1570 table=(QuantizationTable *) RelinquishMagickMemory(table);
1571 return(table);
1572}
1573
cristy3ed852e2009-09-05 21:47:34 +00001574static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1575{
1576 DestinationManager
1577 *destination;
1578
1579 destination=(DestinationManager *) cinfo->dest;
1580 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1581 MaxBufferExtent,destination->buffer);
1582 if (destination->manager.free_in_buffer != MaxBufferExtent)
1583 ERREXIT(cinfo,JERR_FILE_WRITE);
1584 destination->manager.next_output_byte=destination->buffer;
1585 return(TRUE);
1586}
1587
cristy1b58f252012-03-01 01:41:41 +00001588static QuantizationTable *GetQuantizationTable(const char *filename,
1589 const char *slot,ExceptionInfo *exception)
1590{
1591 char
1592 *p,
1593 *xml;
1594
1595 const char
1596 *attribute,
1597 *content;
1598
1599 double
1600 value;
1601
1602 register ssize_t
1603 i;
1604
1605 ssize_t
1606 j;
1607
1608 QuantizationTable
1609 *table;
1610
cristy1fcc8b62012-03-02 17:25:55 +00001611 size_t
1612 length;
1613
cristy1b58f252012-03-01 01:41:41 +00001614 XMLTreeInfo
1615 *description,
1616 *levels,
1617 *quantization_tables,
1618 *table_iterator;
1619
1620 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1621 "Loading quantization tables \"%s\" ...",filename);
1622 table=(QuantizationTable *) NULL;
1623 xml=FileToString(filename,~0,exception);
1624 if (xml == (char *) NULL)
1625 return(table);
1626 quantization_tables=NewXMLTree(xml,exception);
1627 if (quantization_tables == (XMLTreeInfo *) NULL)
1628 {
1629 xml=DestroyString(xml);
1630 return(table);
1631 }
1632 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1633 table_iterator != (XMLTreeInfo *) NULL;
1634 table_iterator=GetNextXMLTreeTag(table_iterator))
1635 {
1636 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1637 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1638 break;
1639 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1640 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1641 break;
1642 }
1643 if (table_iterator == (XMLTreeInfo *) NULL)
1644 {
1645 xml=DestroyString(xml);
1646 return(table);
1647 }
1648 description=GetXMLTreeChild(table_iterator,"description");
1649 if (description == (XMLTreeInfo *) NULL)
1650 {
1651 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1652 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1653 quantization_tables=DestroyXMLTree(quantization_tables);
1654 xml=DestroyString(xml);
1655 return(table);
1656 }
1657 levels=GetXMLTreeChild(table_iterator,"levels");
1658 if (levels == (XMLTreeInfo *) NULL)
1659 {
1660 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1661 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1662 quantization_tables=DestroyXMLTree(quantization_tables);
1663 xml=DestroyString(xml);
1664 return(table);
1665 }
1666 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1667 if (table == (QuantizationTable *) NULL)
1668 ThrowFatalException(ResourceLimitFatalError,
1669 "UnableToAcquireQuantizationTable");
1670 table->slot=(char *) NULL;
1671 table->description=(char *) NULL;
1672 table->levels=(unsigned int *) NULL;
1673 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1674 if (attribute != (char *) NULL)
1675 table->slot=ConstantString(attribute);
1676 content=GetXMLTreeContent(description);
1677 if (content != (char *) NULL)
1678 table->description=ConstantString(content);
1679 attribute=GetXMLTreeAttribute(levels,"width");
1680 if (attribute == (char *) NULL)
1681 {
1682 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1683 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1684 quantization_tables=DestroyXMLTree(quantization_tables);
1685 table=DestroyQuantizationTable(table);
1686 xml=DestroyString(xml);
1687 return(table);
1688 }
1689 table->width=StringToUnsignedLong(attribute);
1690 if (table->width == 0)
1691 {
1692 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1693 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1694 quantization_tables=DestroyXMLTree(quantization_tables);
1695 table=DestroyQuantizationTable(table);
1696 xml=DestroyString(xml);
1697 return(table);
1698 }
1699 attribute=GetXMLTreeAttribute(levels,"height");
1700 if (attribute == (char *) NULL)
1701 {
1702 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1703 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1704 quantization_tables=DestroyXMLTree(quantization_tables);
1705 table=DestroyQuantizationTable(table);
1706 xml=DestroyString(xml);
1707 return(table);
1708 }
1709 table->height=StringToUnsignedLong(attribute);
1710 if (table->height == 0)
1711 {
1712 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1713 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1714 quantization_tables=DestroyXMLTree(quantization_tables);
1715 table=DestroyQuantizationTable(table);
1716 xml=DestroyString(xml);
1717 return(table);
1718 }
1719 attribute=GetXMLTreeAttribute(levels,"divisor");
1720 if (attribute == (char *) NULL)
1721 {
1722 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1723 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1724 quantization_tables=DestroyXMLTree(quantization_tables);
1725 table=DestroyQuantizationTable(table);
1726 xml=DestroyString(xml);
1727 return(table);
1728 }
cristy043f3f32012-03-02 17:37:28 +00001729 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1730 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001731 {
1732 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1733 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1734 quantization_tables=DestroyXMLTree(quantization_tables);
1735 table=DestroyQuantizationTable(table);
1736 xml=DestroyString(xml);
1737 return(table);
1738 }
1739 content=GetXMLTreeContent(levels);
1740 if (content == (char *) NULL)
1741 {
1742 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1743 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1744 quantization_tables=DestroyXMLTree(quantization_tables);
1745 table=DestroyQuantizationTable(table);
1746 xml=DestroyString(xml);
1747 return(table);
1748 }
cristy1fcc8b62012-03-02 17:25:55 +00001749 length=(size_t) table->width*table->height;
1750 if (length < 64)
1751 length=64;
cristy092006b2012-03-02 17:26:02 +00001752 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001753 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001754 if (table->levels == (unsigned int *) NULL)
1755 ThrowFatalException(ResourceLimitFatalError,
1756 "UnableToAcquireQuantizationTable");
1757 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1758 {
cristy043f3f32012-03-02 17:37:28 +00001759 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1760 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001761 while (isspace((int) ((unsigned char) *p)) != 0)
1762 p++;
1763 if (*p == ',')
1764 p++;
1765 content=p;
1766 }
cristy043f3f32012-03-02 17:37:28 +00001767 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001768 (void) value;
1769 if (p != content)
1770 {
1771 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1772 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1773 quantization_tables=DestroyXMLTree(quantization_tables);
1774 table=DestroyQuantizationTable(table);
1775 xml=DestroyString(xml);
1776 return(table);
1777 }
cristy043f3f32012-03-02 17:37:28 +00001778 for (j=i; j < 64; j++)
1779 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001780 quantization_tables=DestroyXMLTree(quantization_tables);
1781 xml=DestroyString(xml);
1782 return(table);
1783}
1784
cristy3ed852e2009-09-05 21:47:34 +00001785static void InitializeDestination(j_compress_ptr cinfo)
1786{
1787 DestinationManager
1788 *destination;
1789
1790 destination=(DestinationManager *) cinfo->dest;
1791 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1792 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1793 destination->manager.next_output_byte=destination->buffer;
1794 destination->manager.free_in_buffer=MaxBufferExtent;
1795}
1796
1797static inline size_t MagickMin(const size_t x,const size_t y)
1798{
1799 if (x < y)
1800 return(x);
1801 return(y);
1802}
1803
1804static void TerminateDestination(j_compress_ptr cinfo)
1805{
1806 DestinationManager
1807 *destination;
1808
1809 destination=(DestinationManager *) cinfo->dest;
1810 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1811 {
1812 ssize_t
1813 count;
1814
1815 count=WriteBlob(destination->image,MaxBufferExtent-
1816 destination->manager.free_in_buffer,destination->buffer);
1817 if (count != (ssize_t)
1818 (MaxBufferExtent-destination->manager.free_in_buffer))
1819 ERREXIT(cinfo,JERR_FILE_WRITE);
1820 }
1821}
1822
1823static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1824{
1825 const char
1826 *name;
1827
1828 const StringInfo
1829 *profile;
1830
1831 MagickBooleanType
1832 iptc;
1833
cristybb503372010-05-27 20:51:26 +00001834 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001835 i;
1836
1837 size_t
cristy524222d2011-04-25 00:37:06 +00001838 length,
1839 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001840
1841 StringInfo
1842 *custom_profile;
1843
cristy3ed852e2009-09-05 21:47:34 +00001844 /*
1845 Save image profile as a APP marker.
1846 */
1847 iptc=MagickFalse;
1848 custom_profile=AcquireStringInfo(65535L);
1849 ResetImageProfileIterator(image);
1850 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1851 {
cristy109e5572010-09-16 18:38:17 +00001852 register unsigned char
1853 *p;
1854
cristy3ed852e2009-09-05 21:47:34 +00001855 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001856 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001857 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001858 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001859 {
1860 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1861 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1862 (unsigned int) length);
1863 }
1864 if (LocaleCompare(name,"ICC") == 0)
1865 {
1866 register unsigned char
1867 *p;
1868
1869 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001870 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001871 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001872 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001873 {
1874 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001875 p[12]=(unsigned char) ((i/65519L)+1);
1876 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1877 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1878 length);
1879 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1880 custom_profile),(unsigned int) (length+tag_length));
1881 }
1882 }
1883 if (((LocaleCompare(name,"IPTC") == 0) ||
1884 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1885 {
cristybb503372010-05-27 20:51:26 +00001886 size_t
cristy3ed852e2009-09-05 21:47:34 +00001887 roundup;
1888
1889 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001890 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001891 {
1892 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001893 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001894 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1895 {
1896 (void) memcpy(p,"Photoshop 3.0 ",14);
1897 tag_length=14;
1898 }
1899 else
1900 {
1901 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1902 tag_length=26;
1903 p[24]=(unsigned char) (length >> 8);
1904 p[25]=(unsigned char) (length & 0xff);
1905 }
1906 p[13]=0x00;
1907 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001908 if (roundup != 0)
1909 p[length+tag_length]='\0';
1910 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1911 custom_profile),(unsigned int) (length+tag_length+roundup));
1912 }
1913 }
1914 if (LocaleCompare(name,"XMP") == 0)
1915 {
1916 StringInfo
1917 *xmp_profile;
1918
1919 /*
1920 Add namespace to XMP profile.
1921 */
cristy0615f0e2011-10-12 11:36:46 +00001922 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy3ed852e2009-09-05 21:47:34 +00001923 ConcatenateStringInfo(xmp_profile,profile);
1924 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001925 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001926 {
1927 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1928 jpeg_write_marker(jpeg_info,XML_MARKER,
1929 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1930 }
1931 xmp_profile=DestroyStringInfo(xmp_profile);
1932 }
cristye8c25f92010-06-03 00:53:06 +00001933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1934 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001935 name=GetNextImageProfile(image);
1936 }
1937 custom_profile=DestroyStringInfo(custom_profile);
1938}
1939
1940static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1941{
1942 DestinationManager
1943 *destination;
1944
1945 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1946 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1947 destination=(DestinationManager *) cinfo->dest;
1948 destination->manager.init_destination=InitializeDestination;
1949 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1950 destination->manager.term_destination=TerminateDestination;
1951 destination->image=image;
1952}
1953
1954static char **SamplingFactorToList(const char *text)
1955{
1956 char
1957 **textlist;
1958
1959 register char
1960 *q;
1961
1962 register const char
1963 *p;
1964
cristybb503372010-05-27 20:51:26 +00001965 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001966 i;
1967
cristybb503372010-05-27 20:51:26 +00001968 size_t
cristy3ed852e2009-09-05 21:47:34 +00001969 lines;
1970
1971 if (text == (char *) NULL)
1972 return((char **) NULL);
1973 /*
1974 Convert string to an ASCII list.
1975 */
1976 lines=1;
1977 for (p=text; *p != '\0'; p++)
1978 if (*p == ',')
1979 lines++;
1980 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1981 sizeof(*textlist));
1982 if (textlist == (char **) NULL)
1983 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1984 p=text;
cristybb503372010-05-27 20:51:26 +00001985 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001986 {
1987 for (q=(char *) p; *q != '\0'; q++)
1988 if (*q == ',')
1989 break;
1990 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1991 sizeof(*textlist[i]));
1992 if (textlist[i] == (char *) NULL)
1993 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1994 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1995 if (*q == '\r')
1996 q++;
1997 p=q+1;
1998 }
1999 textlist[i]=(char *) NULL;
2000 return(textlist);
2001}
2002
2003static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002004 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002005{
2006 const char
2007 *option,
2008 *sampling_factor,
2009 *value;
2010
2011 ErrorManager
2012 error_manager;
2013
cristy21749e92012-02-18 02:29:21 +00002014 int
2015 quality;
2016
cristy3ed852e2009-09-05 21:47:34 +00002017 JSAMPLE
2018 *jpeg_pixels;
2019
2020 JSAMPROW
2021 scanline[1];
2022
cristy3ed852e2009-09-05 21:47:34 +00002023 MagickBooleanType
2024 status;
2025
2026 register JSAMPLE
2027 *q;
2028
cristybb503372010-05-27 20:51:26 +00002029 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002030 i;
2031
cristy524222d2011-04-25 00:37:06 +00002032 ssize_t
2033 y;
2034
cristy3ed852e2009-09-05 21:47:34 +00002035 struct jpeg_compress_struct
2036 jpeg_info;
2037
2038 struct jpeg_error_mgr
2039 jpeg_error;
2040
2041 /*
2042 Open image file.
2043 */
2044 assert(image_info != (const ImageInfo *) NULL);
2045 assert(image_info->signature == MagickSignature);
2046 assert(image != (Image *) NULL);
2047 assert(image->signature == MagickSignature);
2048 if (image->debug != MagickFalse)
2049 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002050 assert(exception != (ExceptionInfo *) NULL);
2051 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00002052 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002053 if (status == MagickFalse)
2054 return(status);
2055 /*
2056 Initialize JPEG parameters.
2057 */
cristy91044972011-04-22 14:21:16 +00002058 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002059 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2060 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2061 jpeg_info.client_data=(void *) image;
2062 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002063 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002064 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002065 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002066 error_manager.image=image;
2067 jpeg_pixels=(JSAMPLE *) NULL;
2068 if (setjmp(error_manager.error_recovery) != 0)
2069 {
2070 jpeg_destroy_compress(&jpeg_info);
2071 (void) CloseBlob(image);
2072 return(MagickFalse);
2073 }
2074 jpeg_info.client_data=(void *) &error_manager;
2075 jpeg_create_compress(&jpeg_info);
2076 JPEGDestinationManager(&jpeg_info,image);
2077 if ((image->columns != (unsigned int) image->columns) ||
2078 (image->rows != (unsigned int) image->rows))
2079 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2080 jpeg_info.image_width=(unsigned int) image->columns;
2081 jpeg_info.image_height=(unsigned int) image->rows;
2082 jpeg_info.input_components=3;
2083 jpeg_info.data_precision=8;
2084 jpeg_info.in_color_space=JCS_RGB;
2085 switch (image->colorspace)
2086 {
2087 case CMYKColorspace:
2088 {
2089 jpeg_info.input_components=4;
2090 jpeg_info.in_color_space=JCS_CMYK;
2091 break;
2092 }
2093 case YCbCrColorspace:
2094 case Rec601YCbCrColorspace:
2095 case Rec709YCbCrColorspace:
2096 {
2097 jpeg_info.in_color_space=JCS_YCbCr;
2098 break;
2099 }
2100 case GRAYColorspace:
2101 case Rec601LumaColorspace:
2102 case Rec709LumaColorspace:
2103 {
2104 jpeg_info.input_components=1;
2105 jpeg_info.in_color_space=JCS_GRAYSCALE;
2106 break;
2107 }
2108 default:
cristy9f396782009-12-21 01:37:45 +00002109 {
cristy3d9f5ba2012-06-26 13:37:31 +00002110 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +00002111 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00002112 break;
cristy9f396782009-12-21 01:37:45 +00002113 }
cristy3ed852e2009-09-05 21:47:34 +00002114 }
2115 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00002116 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002117 {
2118 jpeg_info.input_components=1;
2119 jpeg_info.in_color_space=JCS_GRAYSCALE;
2120 }
2121 jpeg_set_defaults(&jpeg_info);
2122 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2123 jpeg_info.data_precision=8;
2124 else
2125 if (sizeof(JSAMPLE) > 1)
2126 jpeg_info.data_precision=12;
2127 jpeg_info.density_unit=(UINT8) 1;
2128 if (image->debug != MagickFalse)
2129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002130 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2131 floor(image->resolution.y+0.5));
2132 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002133 {
2134 /*
2135 Set image resolution.
2136 */
2137 jpeg_info.write_JFIF_header=MagickTrue;
cristy2a11bef2011-10-28 18:33:11 +00002138 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2139 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002140 if (image->units == PixelsPerInchResolution)
2141 jpeg_info.density_unit=(UINT8) 1;
2142 if (image->units == PixelsPerCentimeterResolution)
2143 jpeg_info.density_unit=(UINT8) 2;
2144 }
cristy97cb3bf2012-02-21 18:31:20 +00002145 jpeg_info.dct_method=JDCT_FLOAT;
cristy3ed852e2009-09-05 21:47:34 +00002146 option=GetImageOption(image_info,"jpeg:dct-method");
2147 if (option != (const char *) NULL)
2148 switch (*option)
2149 {
2150 case 'D':
2151 case 'd':
2152 {
2153 if (LocaleCompare(option,"default") == 0)
2154 jpeg_info.dct_method=JDCT_DEFAULT;
2155 break;
2156 }
2157 case 'F':
2158 case 'f':
2159 {
2160 if (LocaleCompare(option,"fastest") == 0)
2161 jpeg_info.dct_method=JDCT_FASTEST;
2162 if (LocaleCompare(option,"float") == 0)
2163 jpeg_info.dct_method=JDCT_FLOAT;
2164 break;
2165 }
2166 case 'I':
2167 case 'i':
2168 {
2169 if (LocaleCompare(option,"ifast") == 0)
2170 jpeg_info.dct_method=JDCT_IFAST;
2171 if (LocaleCompare(option,"islow") == 0)
2172 jpeg_info.dct_method=JDCT_ISLOW;
2173 break;
2174 }
2175 }
2176 option=GetImageOption(image_info,"jpeg:optimize-coding");
2177 if (option != (const char *) NULL)
anthony6f201312012-03-30 04:08:15 +00002178 jpeg_info.optimize_coding=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00002179 else
2180 {
2181 MagickSizeType
2182 length;
2183
2184 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2185 image->rows*sizeof(JSAMPLE);
2186 if (length == (MagickSizeType) ((size_t) length))
2187 {
2188 /*
2189 Perform optimization only if available memory resources permit it.
2190 */
2191 status=AcquireMagickResource(MemoryResource,length);
cristy3ed852e2009-09-05 21:47:34 +00002192 RelinquishMagickResource(MemoryResource,length);
anthony6f201312012-03-30 04:08:15 +00002193 jpeg_info.optimize_coding=status;
cristy3ed852e2009-09-05 21:47:34 +00002194 }
2195 }
2196#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2197 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2198 (image_info->interlace != NoInterlace))
2199 {
2200 if (image->debug != MagickFalse)
2201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2202 "Interlace: progressive");
2203 jpeg_simple_progression(&jpeg_info);
2204 }
2205 else
2206 if (image->debug != MagickFalse)
2207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2208 "Interlace: non-progressive");
2209#else
2210 if (image->debug != MagickFalse)
2211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2212 "Interlace: nonprogressive");
2213#endif
cristy0adb4f92009-11-28 18:08:51 +00002214 option=GetImageOption(image_info,"jpeg:extent");
2215 if (option != (const char *) NULL)
2216 {
2217 Image
2218 *jpeg_image;
2219
2220 ImageInfo
2221 *jpeg_info;
2222
2223 jpeg_info=CloneImageInfo(image_info);
cristy1e178e72011-08-28 19:44:34 +00002224 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002225 if (jpeg_image != (Image *) NULL)
2226 {
2227 MagickSizeType
2228 extent;
2229
2230 size_t
cristy87e73ab2010-02-12 01:59:12 +00002231 maximum,
2232 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002233
2234 /*
2235 Search for compression quality that does not exceed image extent.
2236 */
2237 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002238 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002239 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
2240 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00002241 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00002242 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002243 {
cristy87e73ab2010-02-12 01:59:12 +00002244 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy1e178e72011-08-28 19:44:34 +00002245 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002246 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00002247 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002248 else
cristy87e73ab2010-02-12 01:59:12 +00002249 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00002250 }
2251 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00002252 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002253 jpeg_image=DestroyImage(jpeg_image);
2254 }
2255 jpeg_info=DestroyImageInfo(jpeg_info);
2256 }
cristy21749e92012-02-18 02:29:21 +00002257 quality=92;
cristy3ed852e2009-09-05 21:47:34 +00002258 if ((image_info->compression != LosslessJPEGCompression) &&
2259 (image->quality <= 100))
2260 {
cristy21749e92012-02-18 02:29:21 +00002261 if (image->quality != UndefinedCompressionQuality)
2262 quality=(int) image->quality;
cristy3ed852e2009-09-05 21:47:34 +00002263 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00002264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2265 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00002266 }
2267 else
2268 {
2269#if !defined(C_LOSSLESS_SUPPORTED)
cristy21749e92012-02-18 02:29:21 +00002270 quality=100;
cristy3ed852e2009-09-05 21:47:34 +00002271 if (image->debug != MagickFalse)
2272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2273#else
2274 if (image->quality < 100)
cristy1e178e72011-08-28 19:44:34 +00002275 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2276 "LosslessToLossyJPEGConversion",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002277 else
2278 {
2279 int
2280 point_transform,
2281 predictor;
2282
2283 predictor=image->quality/100; /* range 1-7 */
2284 point_transform=image->quality % 20; /* range 0-15 */
2285 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2286 if (image->debug != MagickFalse)
2287 {
2288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2289 "Compression: lossless");
2290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2291 "Predictor: %d",predictor);
2292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2293 "Point Transform: %d",point_transform);
2294 }
2295 }
2296#endif
2297 }
cristy21749e92012-02-18 02:29:21 +00002298 jpeg_set_quality(&jpeg_info,quality,MagickTrue);
cristy16e74722012-07-01 13:01:38 +00002299#if (JPEG_LIB_VERSION >= 70)
2300 option=GetImageOption(image_info,"quality");
2301 if (option != (const char *) NULL)
2302 {
2303 GeometryInfo
2304 geometry_info;
2305
2306 int
2307 flags;
2308
2309 /*
2310 Set quality scaling for luminance and chrominance separately.
2311 */
2312 flags=ParseGeometry(option,&geometry_info);
2313 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2314 {
2315 jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
2316 (geometry_info.rho+0.5));
2317 jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
2318 (geometry_info.sigma+0.5));
2319 jpeg_default_qtables(&jpeg_info,MagickTrue);
2320 }
2321 }
2322#endif
cristy3ed852e2009-09-05 21:47:34 +00002323 sampling_factor=(const char *) NULL;
cristyd15e6592011-10-15 00:13:06 +00002324 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
cristy3ed852e2009-09-05 21:47:34 +00002325 if (value != (char *) NULL)
2326 {
2327 sampling_factor=value;
2328 if (image->debug != MagickFalse)
2329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2330 " Input sampling-factors=%s",sampling_factor);
2331 }
2332 if (image_info->sampling_factor != (char *) NULL)
2333 sampling_factor=image_info->sampling_factor;
2334 if (sampling_factor == (const char *) NULL)
2335 {
2336 if (image->quality >= 90)
2337 for (i=0; i < MAX_COMPONENTS; i++)
2338 {
2339 jpeg_info.comp_info[i].h_samp_factor=1;
2340 jpeg_info.comp_info[i].v_samp_factor=1;
2341 }
2342 }
2343 else
2344 {
2345 char
2346 **factors;
2347
2348 GeometryInfo
2349 geometry_info;
2350
2351 MagickStatusType
2352 flags;
2353
2354 /*
2355 Set sampling factor.
2356 */
2357 i=0;
2358 factors=SamplingFactorToList(sampling_factor);
2359 if (factors != (char **) NULL)
2360 {
2361 for (i=0; i < MAX_COMPONENTS; i++)
2362 {
2363 if (factors[i] == (char *) NULL)
2364 break;
2365 flags=ParseGeometry(factors[i],&geometry_info);
2366 if ((flags & SigmaValue) == 0)
2367 geometry_info.sigma=geometry_info.rho;
2368 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2369 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2370 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2371 }
2372 factors=(char **) RelinquishMagickMemory(factors);
2373 }
2374 for ( ; i < MAX_COMPONENTS; i++)
2375 {
2376 jpeg_info.comp_info[i].h_samp_factor=1;
2377 jpeg_info.comp_info[i].v_samp_factor=1;
2378 }
2379 }
2380 if (jpeg_info.input_components == 1)
2381 for (i=0; i < MAX_COMPONENTS; i++)
2382 {
2383 jpeg_info.comp_info[i].h_samp_factor=1;
2384 jpeg_info.comp_info[i].v_samp_factor=1;
2385 }
cristy1b58f252012-03-01 01:41:41 +00002386 option=GetImageOption(image_info,"jpeg:q-table");
2387 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002388 {
cristy1b58f252012-03-01 01:41:41 +00002389 QuantizationTable
2390 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002391
cristy1445b322012-02-19 18:57:30 +00002392 /*
cristy1b58f252012-03-01 01:41:41 +00002393 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002394 */
cristy1b58f252012-03-01 01:41:41 +00002395 table=GetQuantizationTable(option,"0",exception);
2396 if (table != (QuantizationTable *) NULL)
2397 {
2398 jpeg_add_quant_table(&jpeg_info,0,table->levels,jpeg_quality_scaling(
2399 quality),0);
2400 table=DestroyQuantizationTable(table);
2401 }
2402 table=GetQuantizationTable(option,"1",exception);
2403 if (table != (QuantizationTable *) NULL)
2404 {
2405 jpeg_add_quant_table(&jpeg_info,1,table->levels,jpeg_quality_scaling(
2406 quality),0);
2407 table=DestroyQuantizationTable(table);
2408 }
2409 table=GetQuantizationTable(option,"2",exception);
2410 if (table != (QuantizationTable *) NULL)
2411 {
2412 jpeg_add_quant_table(&jpeg_info,2,table->levels,jpeg_quality_scaling(
2413 quality),0);
2414 table=DestroyQuantizationTable(table);
2415 }
2416 table=GetQuantizationTable(option,"3",exception);
2417 if (table != (QuantizationTable *) NULL)
2418 {
2419 jpeg_add_quant_table(&jpeg_info,3,table->levels,jpeg_quality_scaling(
2420 quality),0);
2421 table=DestroyQuantizationTable(table);
2422 }
cristy1445b322012-02-19 18:57:30 +00002423 }
cristy3ed852e2009-09-05 21:47:34 +00002424 jpeg_start_compress(&jpeg_info,MagickTrue);
2425 if (image->debug != MagickFalse)
2426 {
2427 if (image->storage_class == PseudoClass)
2428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2429 "Storage class: PseudoClass");
2430 else
2431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2432 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2434 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002435 if (image->colors != 0)
2436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002437 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002438 else
2439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2440 "Number of colors: unspecified");
2441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2442 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2443 switch (image->colorspace)
2444 {
2445 case CMYKColorspace:
2446 {
2447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2448 "Storage class: DirectClass");
2449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2450 "Colorspace: CMYK");
2451 break;
2452 }
2453 case YCbCrColorspace:
2454 case Rec601YCbCrColorspace:
2455 case Rec709YCbCrColorspace:
2456 {
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2458 "Colorspace: YCbCr");
2459 break;
2460 }
2461 default:
2462 break;
2463 }
2464 switch (image->colorspace)
2465 {
2466 case CMYKColorspace:
2467 {
2468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2469 "Colorspace: CMYK");
2470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2471 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2472 jpeg_info.comp_info[0].h_samp_factor,
2473 jpeg_info.comp_info[0].v_samp_factor,
2474 jpeg_info.comp_info[1].h_samp_factor,
2475 jpeg_info.comp_info[1].v_samp_factor,
2476 jpeg_info.comp_info[2].h_samp_factor,
2477 jpeg_info.comp_info[2].v_samp_factor,
2478 jpeg_info.comp_info[3].h_samp_factor,
2479 jpeg_info.comp_info[3].v_samp_factor);
2480 break;
2481 }
2482 case GRAYColorspace:
2483 case Rec601LumaColorspace:
2484 case Rec709LumaColorspace:
2485 {
2486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2487 "Colorspace: GRAY");
2488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2489 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2490 jpeg_info.comp_info[0].v_samp_factor);
2491 break;
2492 }
2493 case RGBColorspace:
2494 {
2495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2496 "Image colorspace is RGB");
2497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2498 "Sampling factors: %dx%d,%dx%d,%dx%d",
2499 jpeg_info.comp_info[0].h_samp_factor,
2500 jpeg_info.comp_info[0].v_samp_factor,
2501 jpeg_info.comp_info[1].h_samp_factor,
2502 jpeg_info.comp_info[1].v_samp_factor,
2503 jpeg_info.comp_info[2].h_samp_factor,
2504 jpeg_info.comp_info[2].v_samp_factor);
2505 break;
2506 }
2507 case YCbCrColorspace:
2508 case Rec601YCbCrColorspace:
2509 case Rec709YCbCrColorspace:
2510 {
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 "Colorspace: YCbCr");
2513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2514 "Sampling factors: %dx%d,%dx%d,%dx%d",
2515 jpeg_info.comp_info[0].h_samp_factor,
2516 jpeg_info.comp_info[0].v_samp_factor,
2517 jpeg_info.comp_info[1].h_samp_factor,
2518 jpeg_info.comp_info[1].v_samp_factor,
2519 jpeg_info.comp_info[2].h_samp_factor,
2520 jpeg_info.comp_info[2].v_samp_factor);
2521 break;
2522 }
2523 default:
2524 {
2525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2526 image->colorspace);
2527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2528 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2529 jpeg_info.comp_info[0].h_samp_factor,
2530 jpeg_info.comp_info[0].v_samp_factor,
2531 jpeg_info.comp_info[1].h_samp_factor,
2532 jpeg_info.comp_info[1].v_samp_factor,
2533 jpeg_info.comp_info[2].h_samp_factor,
2534 jpeg_info.comp_info[2].v_samp_factor,
2535 jpeg_info.comp_info[3].h_samp_factor,
2536 jpeg_info.comp_info[3].v_samp_factor);
2537 break;
2538 }
2539 }
2540 }
2541 /*
2542 Write JPEG profiles.
2543 */
cristyd15e6592011-10-15 00:13:06 +00002544 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002545 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002546 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002547 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2548 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2549 if (image->profiles != (void *) NULL)
2550 WriteProfile(&jpeg_info,image);
2551 /*
2552 Convert MIFF to JPEG raster pixels.
2553 */
2554 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2555 jpeg_info.input_components*sizeof(*jpeg_pixels));
2556 if (jpeg_pixels == (JSAMPLE *) NULL)
2557 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2558 if (setjmp(error_manager.error_recovery) != 0)
2559 {
2560 jpeg_destroy_compress(&jpeg_info);
2561 if (jpeg_pixels != (unsigned char *) NULL)
2562 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2563 (void) CloseBlob(image);
2564 return(MagickFalse);
2565 }
2566 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002567 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002568 {
cristy3ed852e2009-09-05 21:47:34 +00002569 if ((jpeg_info.in_color_space == JCS_RGB) ||
2570 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002571 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002572 {
cristy4c08aed2011-07-01 19:47:50 +00002573 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002574 *p;
2575
cristybb503372010-05-27 20:51:26 +00002576 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002577 x;
2578
cristy1e178e72011-08-28 19:44:34 +00002579 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002580 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002581 break;
2582 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002583 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002584 {
cristy4c08aed2011-07-01 19:47:50 +00002585 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2586 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2587 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002588 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002589 }
2590 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002591 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2592 image->rows);
2593 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002594 break;
2595 }
2596 else
cristye90d7402010-03-14 18:21:29 +00002597 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002598 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002599 {
cristy4c08aed2011-07-01 19:47:50 +00002600 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002601 *p;
2602
cristybb503372010-05-27 20:51:26 +00002603 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002604 x;
2605
cristy1e178e72011-08-28 19:44:34 +00002606 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002607 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002608 break;
2609 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002610 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002611 {
cristy4c08aed2011-07-01 19:47:50 +00002612 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristyed231572011-07-14 02:18:59 +00002613 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002614 }
2615 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002616 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2617 image->rows);
2618 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002619 break;
2620 }
2621 else
cristybb503372010-05-27 20:51:26 +00002622 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002623 {
cristy4c08aed2011-07-01 19:47:50 +00002624 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002625 *p;
2626
cristybb503372010-05-27 20:51:26 +00002627 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002628 x;
2629
cristy1e178e72011-08-28 19:44:34 +00002630 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002631 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002632 break;
2633 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002634 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002635 {
2636 /*
2637 Convert DirectClass packets to contiguous CMYK scanlines.
2638 */
2639 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002640 GetPixelRed(image,p))));
cristye90d7402010-03-14 18:21:29 +00002641 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002642 GetPixelGreen(image,p))));
cristye90d7402010-03-14 18:21:29 +00002643 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002644 GetPixelBlue(image,p))));
cristye90d7402010-03-14 18:21:29 +00002645 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002646 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002647 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002648 }
2649 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002650 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2651 image->rows);
2652 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002653 break;
2654 }
2655 }
2656 else
2657 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002658 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002659 {
cristy4c08aed2011-07-01 19:47:50 +00002660 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002661 *p;
2662
cristybb503372010-05-27 20:51:26 +00002663 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002664 x;
2665
cristy1e178e72011-08-28 19:44:34 +00002666 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002667 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002668 break;
2669 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002670 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002671 {
cristy1fcc8b62012-03-02 17:25:55 +00002672 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002673 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002674 }
2675 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002676 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2677 image->rows);
2678 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002679 break;
2680 }
2681 else
2682 if ((jpeg_info.in_color_space == JCS_RGB) ||
2683 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002684 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002685 {
cristy4c08aed2011-07-01 19:47:50 +00002686 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002687 *p;
2688
cristybb503372010-05-27 20:51:26 +00002689 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002690 x;
2691
cristy1e178e72011-08-28 19:44:34 +00002692 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002693 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002694 break;
2695 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002696 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002697 {
cristy4c08aed2011-07-01 19:47:50 +00002698 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2699 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2700 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002701 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002702 }
2703 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002704 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002705 image->rows);
cristye90d7402010-03-14 18:21:29 +00002706 if (status == MagickFalse)
2707 break;
2708 }
2709 else
cristybb503372010-05-27 20:51:26 +00002710 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002711 {
cristy4c08aed2011-07-01 19:47:50 +00002712 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002713 *p;
2714
cristybb503372010-05-27 20:51:26 +00002715 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002716 x;
2717
cristy1e178e72011-08-28 19:44:34 +00002718 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002719 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002720 break;
2721 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002722 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002723 {
2724 /*
2725 Convert DirectClass packets to contiguous CMYK scanlines.
2726 */
cristye90d7402010-03-14 18:21:29 +00002727 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002728 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002729 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002730 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002731 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002732 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002733 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002734 GetPixelBlack(image,p)) >> 4));
cristyed231572011-07-14 02:18:59 +00002735 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002736 }
2737 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002738 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002739 image->rows);
cristye90d7402010-03-14 18:21:29 +00002740 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002741 break;
2742 }
cristybb503372010-05-27 20:51:26 +00002743 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002744 jpeg_finish_compress(&jpeg_info);
2745 /*
2746 Relinquish resources.
2747 */
2748 jpeg_destroy_compress(&jpeg_info);
2749 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2750 (void) CloseBlob(image);
2751 return(MagickTrue);
2752}
2753#endif