blob: bc29af4f4798facda3c325809b65c868be8f58b6 [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)
104#define MaxBufferExtent 8192
105
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 {
251 if (source->start_of_blob != 0)
252 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);
cristyd28b1dd2011-05-14 20:30:38 +0000359 if ((jpeg_info->err->num_warnings == 0) ||
360 (jpeg_info->err->trace_level >= 3))
361 ThrowBinaryException(CorruptImageWarning,(char *) message,
362 image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000363 }
364 else
365 if ((image->debug != MagickFalse) &&
366 (level >= jpeg_info->err->trace_level))
367 {
368 /*
369 Process trace message.
370 */
371 (jpeg_info->err->format_message)(jpeg_info,message);
372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
373 "[%s] JPEG Trace: \"%s\"",image->filename,message);
374 }
375 return(MagickTrue);
376}
377
cristy3ed852e2009-09-05 21:47:34 +0000378static boolean ReadComment(j_decompress_ptr jpeg_info)
379{
380 char
381 *comment;
382
383 ErrorManager
384 *error_manager;
385
cristy018f07f2011-09-04 21:15:19 +0000386 ExceptionInfo
387 *exception;
388
cristy3ed852e2009-09-05 21:47:34 +0000389 Image
390 *image;
391
392 register char
393 *p;
394
cristybb503372010-05-27 20:51:26 +0000395 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000396 i;
397
398 size_t
399 length;
400
401 /*
402 Determine length of comment.
403 */
404 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000405 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000406 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000407 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000408 length+=GetCharacter(jpeg_info);
409 length-=2;
410 if (length <= 0)
411 return(MagickTrue);
412 comment=(char *) NULL;
cristy37e0b382011-06-07 13:31:21 +0000413 if (~length >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000414 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
415 sizeof(*comment));
416 if (comment == (char *) NULL)
417 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
418 image->filename);
419 /*
420 Read comment.
421 */
cristybb503372010-05-27 20:51:26 +0000422 i=(ssize_t) length-1;
cristy3ed852e2009-09-05 21:47:34 +0000423 for (p=comment; i-- >= 0; p++)
424 *p=(char) GetCharacter(jpeg_info);
425 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000426 (void) SetImageProperty(image,"comment",comment,exception);
cristy3ed852e2009-09-05 21:47:34 +0000427 comment=DestroyString(comment);
428 return(MagickTrue);
429}
430
431static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
432{
433 char
434 magick[12];
435
436 ErrorManager
437 *error_manager;
438
cristy018f07f2011-09-04 21:15:19 +0000439 ExceptionInfo
440 *exception;
441
cristy3ed852e2009-09-05 21:47:34 +0000442 Image
443 *image;
444
445 MagickBooleanType
446 status;
447
cristybb503372010-05-27 20:51:26 +0000448 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000449 i;
450
451 register unsigned char
452 *p;
453
454 size_t
455 length;
456
457 StringInfo
458 *icc_profile,
459 *profile;
460
461 /*
462 Read color profile.
463 */
cristybb503372010-05-27 20:51:26 +0000464 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000465 length+=(size_t) GetCharacter(jpeg_info);
466 length-=2;
467 if (length <= 14)
468 {
469 while (length-- > 0)
470 (void) GetCharacter(jpeg_info);
471 return(MagickTrue);
472 }
473 for (i=0; i < 12; i++)
474 magick[i]=(char) GetCharacter(jpeg_info);
475 if (LocaleCompare(magick,ICC_PROFILE) != 0)
476 {
477 /*
478 Not a ICC profile, return.
479 */
cristybb503372010-05-27 20:51:26 +0000480 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000481 (void) GetCharacter(jpeg_info);
482 return(MagickTrue);
483 }
484 (void) GetCharacter(jpeg_info); /* id */
485 (void) GetCharacter(jpeg_info); /* markers */
486 length-=14;
487 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000488 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000489 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000490 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000491 if (profile == (StringInfo *) NULL)
492 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
493 image->filename);
494 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000495 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000496 *p++=(unsigned char) GetCharacter(jpeg_info);
497 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
498 if (icc_profile != (StringInfo *) NULL)
499 {
500 ConcatenateStringInfo(icc_profile,profile);
501 profile=DestroyStringInfo(profile);
502 }
503 else
504 {
cristyd15e6592011-10-15 00:13:06 +0000505 status=SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000506 profile=DestroyStringInfo(profile);
507 if (status == MagickFalse)
508 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
509 image->filename);
510 }
511 if (image->debug != MagickFalse)
512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000513 "Profile: ICC, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000514 return(MagickTrue);
515}
516
517static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
518{
519 char
520 magick[MaxTextExtent];
521
522 ErrorManager
523 *error_manager;
524
cristy018f07f2011-09-04 21:15:19 +0000525 ExceptionInfo
526 *exception;
527
cristy3ed852e2009-09-05 21:47:34 +0000528 Image
529 *image;
530
531 MagickBooleanType
532 status;
533
cristybb503372010-05-27 20:51:26 +0000534 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000535 i;
536
537 register unsigned char
538 *p;
539
540 size_t
541 length;
542
543 StringInfo
544 *iptc_profile,
545 *profile;
546
547 /*
548 Determine length of binary data stored here.
549 */
cristybb503372010-05-27 20:51:26 +0000550 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000551 length+=(size_t) GetCharacter(jpeg_info);
552 length-=2;
553 if (length <= 14)
554 {
555 while (length-- > 0)
556 (void) GetCharacter(jpeg_info);
557 return(MagickTrue);
558 }
559 /*
560 Validate that this was written as a Photoshop resource format slug.
561 */
562 for (i=0; i < 10; i++)
563 magick[i]=(char) GetCharacter(jpeg_info);
564 magick[10]='\0';
565 if (length <= 10)
566 return(MagickTrue);
567 length-=10;
568 if (LocaleCompare(magick,"Photoshop ") != 0)
569 {
570 /*
571 Not a IPTC profile, return.
572 */
cristybb503372010-05-27 20:51:26 +0000573 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000574 (void) GetCharacter(jpeg_info);
575 return(MagickTrue);
576 }
577 /*
578 Remove the version number.
579 */
580 for (i=0; i < 4; i++)
581 (void) GetCharacter(jpeg_info);
582 if (length <= 4)
583 return(MagickTrue);
584 length-=4;
585 if (length == 0)
586 return(MagickTrue);
587 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000588 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000589 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000590 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000591 if (profile == (StringInfo *) NULL)
592 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
593 image->filename);
594 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000595 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000596 *p++=(unsigned char) GetCharacter(jpeg_info);
597 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
598 if (iptc_profile != (StringInfo *) NULL)
599 {
600 ConcatenateStringInfo(iptc_profile,profile);
601 profile=DestroyStringInfo(profile);
602 }
603 else
604 {
cristyd15e6592011-10-15 00:13:06 +0000605 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000606 profile=DestroyStringInfo(profile);
607 if (status == MagickFalse)
608 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
609 image->filename);
610 }
611 if (image->debug != MagickFalse)
612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000613 "Profile: iptc, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000614 return(MagickTrue);
615}
616
617static boolean ReadProfile(j_decompress_ptr jpeg_info)
618{
619 char
620 name[MaxTextExtent];
621
cristye23ec9d2011-08-16 18:15:40 +0000622 const StringInfo
623 *previous_profile;
624
cristy3ed852e2009-09-05 21:47:34 +0000625 ErrorManager
626 *error_manager;
627
cristy018f07f2011-09-04 21:15:19 +0000628 ExceptionInfo
629 *exception;
630
cristy3ed852e2009-09-05 21:47:34 +0000631 Image
632 *image;
633
634 int
635 marker;
636
637 MagickBooleanType
638 status;
639
cristybb503372010-05-27 20:51:26 +0000640 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000641 i;
642
643 register unsigned char
644 *p;
645
646 size_t
647 length;
648
649 StringInfo
650 *profile;
651
652 /*
653 Read generic profile.
654 */
cristybb503372010-05-27 20:51:26 +0000655 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000656 length+=(size_t) GetCharacter(jpeg_info);
657 if (length <= 2)
658 return(MagickTrue);
659 length-=2;
660 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000661 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000662 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000663 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000664 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000665 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000666 if (profile == (StringInfo *) NULL)
667 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
668 image->filename);
669 p=GetStringInfoDatum(profile);
cristy08f255a2011-08-17 01:23:06 +0000670 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000671 *p++=(unsigned char) GetCharacter(jpeg_info);
672 if (marker == 1)
673 {
674 p=GetStringInfoDatum(profile);
675 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
676 (void) CopyMagickString(name,"exif",MaxTextExtent);
677 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
678 {
cristybb503372010-05-27 20:51:26 +0000679 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000680 j;
681
682 /*
683 Extract namespace from XMP profile.
684 */
685 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000686 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000687 {
688 if (*p == '\0')
689 break;
690 p++;
691 }
cristybb503372010-05-27 20:51:26 +0000692 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000693 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
694 (void) CopyMagickString(name,"xmp",MaxTextExtent);
695 }
696 }
cristye23ec9d2011-08-16 18:15:40 +0000697 previous_profile=GetImageProfile(image,name);
698 if (previous_profile != (const StringInfo *) NULL)
cristy08f255a2011-08-17 01:23:06 +0000699 {
cristy1b58f252012-03-01 01:41:41 +0000700 size_t
cristy05c0c9a2011-09-05 23:16:13 +0000701 length;
702
703 length=GetStringInfoLength(profile);
cristy08f255a2011-08-17 01:23:06 +0000704 SetStringInfoLength(profile,GetStringInfoLength(profile)+
705 GetStringInfoLength(previous_profile));
cristy83ab3d82011-09-06 12:05:51 +0000706 (void) memmove(GetStringInfoDatum(profile)+
707 GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
708 length);
cristy08f255a2011-08-17 01:23:06 +0000709 (void) memcpy(GetStringInfoDatum(profile),
710 GetStringInfoDatum(previous_profile),
711 GetStringInfoLength(previous_profile));
712 }
cristyd15e6592011-10-15 00:13:06 +0000713 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000714 profile=DestroyStringInfo(profile);
715 if (status == MagickFalse)
716 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
717 image->filename);
718 if (image->debug != MagickFalse)
719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000720 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000721 return(MagickTrue);
722}
723
cristyf2faecf2010-05-28 19:19:36 +0000724static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000725{
726 SourceManager
727 *source;
728
729 if (number_bytes <= 0)
730 return;
731 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000732 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000733 {
cristyf2faecf2010-05-28 19:19:36 +0000734 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000735 (void) FillInputBuffer(cinfo);
736 }
cristy4cb162a2010-05-30 03:04:47 +0000737 source->manager.next_input_byte+=number_bytes;
738 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000739}
740
741static void TerminateSource(j_decompress_ptr cinfo)
742{
cristydf0d90e2011-12-12 01:03:55 +0000743 (void) cinfo;
cristy3ed852e2009-09-05 21:47:34 +0000744}
745
746static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
747{
748 SourceManager
749 *source;
750
751 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
752 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
753 source=(SourceManager *) cinfo->src;
754 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
755 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
756 source=(SourceManager *) cinfo->src;
757 source->manager.init_source=InitializeSource;
758 source->manager.fill_input_buffer=FillInputBuffer;
759 source->manager.skip_input_data=SkipInputData;
760 source->manager.resync_to_restart=jpeg_resync_to_restart;
761 source->manager.term_source=TerminateSource;
762 source->manager.bytes_in_buffer=0;
763 source->manager.next_input_byte=NULL;
764 source->image=image;
765}
766
767static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
768 Image *image)
769{
770 image->quality=UndefinedCompressionQuality;
771#if defined(D_PROGRESSIVE_SUPPORTED)
772 if (image->compression == LosslessJPEGCompression)
773 {
774 image->quality=100;
775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
776 "Quality: 100 (lossless)");
777 }
778 else
779#endif
780 {
cristybb503372010-05-27 20:51:26 +0000781 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000782 j,
783 qvalue,
784 sum;
785
cristybb503372010-05-27 20:51:26 +0000786 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000787 i;
788
789 /*
790 Determine the JPEG compression quality from the quantization tables.
791 */
792 sum=0;
793 for (i=0; i < NUM_QUANT_TBLS; i++)
794 {
795 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
796 for (j=0; j < DCTSIZE2; j++)
797 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
798 }
799 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
800 (jpeg_info->quant_tbl_ptrs[1] != NULL))
801 {
cristybb503372010-05-27 20:51:26 +0000802 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000803 hash[101] =
804 {
805 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
806 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
807 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
808 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
809 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
810 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
811 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
812 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
813 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
814 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
815 0
816 },
817 sums[101] =
818 {
819 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
820 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
821 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
822 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
823 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
824 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
825 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
826 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
827 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
828 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
829 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
830 128, 0
831 };
832
cristybb503372010-05-27 20:51:26 +0000833 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000834 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
835 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
836 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
837 for (i=0; i < 100; i++)
838 {
839 if ((qvalue < hash[i]) && (sum < sums[i]))
840 continue;
cristyb6c017e2010-01-13 19:11:39 +0000841 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000842 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000843 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000845 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000846 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000847 break;
848 }
849 }
850 else
851 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
852 {
cristybb503372010-05-27 20:51:26 +0000853 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000854 hash[101] =
855 {
856 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
857 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
858 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
859 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
860 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
861 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
862 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
863 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
864 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
865 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
866 0
867 },
868 sums[101] =
869 {
870 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
871 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
872 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
873 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
874 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
875 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
876 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
877 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
878 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
879 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
880 667, 592, 518, 441, 369, 292, 221, 151, 86,
881 64, 0
882 };
883
cristybb503372010-05-27 20:51:26 +0000884 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000885 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
886 for (i=0; i < 100; i++)
887 {
888 if ((qvalue < hash[i]) && (sum < sums[i]))
889 continue;
cristyb6c017e2010-01-13 19:11:39 +0000890 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000891 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000892 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000894 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000895 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000896 break;
897 }
898 }
899 }
900}
901
cristyd15e6592011-10-15 00:13:06 +0000902static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000903{
904 char
905 sampling_factor[MaxTextExtent];
906
907 switch (jpeg_info->out_color_space)
908 {
909 case JCS_CMYK:
910 {
911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000912 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000913 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
914 jpeg_info->comp_info[0].v_samp_factor,
915 jpeg_info->comp_info[1].h_samp_factor,
916 jpeg_info->comp_info[1].v_samp_factor,
917 jpeg_info->comp_info[2].h_samp_factor,
918 jpeg_info->comp_info[2].v_samp_factor,
919 jpeg_info->comp_info[3].h_samp_factor,
920 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000921 break;
cristy3ed852e2009-09-05 21:47:34 +0000922 }
923 case JCS_GRAYSCALE:
924 {
925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
926 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000927 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000928 jpeg_info->comp_info[0].h_samp_factor,
929 jpeg_info->comp_info[0].v_samp_factor);
930 break;
931 }
932 case JCS_RGB:
933 {
934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000935 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000936 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
937 jpeg_info->comp_info[0].v_samp_factor,
938 jpeg_info->comp_info[1].h_samp_factor,
939 jpeg_info->comp_info[1].v_samp_factor,
940 jpeg_info->comp_info[2].h_samp_factor,
941 jpeg_info->comp_info[2].v_samp_factor);
942 break;
943 }
944 default:
945 {
946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
947 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000948 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000949 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
950 jpeg_info->comp_info[0].v_samp_factor,
951 jpeg_info->comp_info[1].h_samp_factor,
952 jpeg_info->comp_info[1].v_samp_factor,
953 jpeg_info->comp_info[2].h_samp_factor,
954 jpeg_info->comp_info[2].v_samp_factor,
955 jpeg_info->comp_info[3].h_samp_factor,
956 jpeg_info->comp_info[3].v_samp_factor);
957 break;
958 }
959 }
cristyd15e6592011-10-15 00:13:06 +0000960 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
961 exception);
cristye90d7402010-03-14 18:21:29 +0000962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
963 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000964}
965
966static Image *ReadJPEGImage(const ImageInfo *image_info,
967 ExceptionInfo *exception)
968{
969 char
970 value[MaxTextExtent];
971
cristycaec74e2009-09-14 02:20:04 +0000972 const char
cristy11151212009-09-14 13:10:15 +0000973 *option;
cristycaec74e2009-09-14 02:20:04 +0000974
cristy3ed852e2009-09-05 21:47:34 +0000975 ErrorManager
976 error_manager;
977
cristy3ed852e2009-09-05 21:47:34 +0000978 Image
979 *image;
980
cristy3ed852e2009-09-05 21:47:34 +0000981 JSAMPLE
982 *jpeg_pixels;
983
984 JSAMPROW
985 scanline[1];
986
987 MagickBooleanType
988 debug,
989 status;
990
991 MagickSizeType
992 number_pixels;
993
cristy4c08aed2011-07-01 19:47:50 +0000994 Quantum
995 index;
996
cristybb503372010-05-27 20:51:26 +0000997 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000998 i;
999
1000 struct jpeg_decompress_struct
1001 jpeg_info;
1002
1003 struct jpeg_error_mgr
1004 jpeg_error;
1005
1006 register JSAMPLE
1007 *p;
1008
cristybb503372010-05-27 20:51:26 +00001009 size_t
cristy3ed852e2009-09-05 21:47:34 +00001010 precision,
1011 units;
1012
cristy524222d2011-04-25 00:37:06 +00001013 ssize_t
1014 y;
1015
cristy3ed852e2009-09-05 21:47:34 +00001016 /*
1017 Open image file.
1018 */
1019 assert(image_info != (const ImageInfo *) NULL);
1020 assert(image_info->signature == MagickSignature);
1021 if (image_info->debug != MagickFalse)
1022 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1023 image_info->filename);
1024 assert(exception != (ExceptionInfo *) NULL);
1025 assert(exception->signature == MagickSignature);
1026 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +00001027 (void) debug;
cristy9950d572011-10-01 18:22:35 +00001028 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001029 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1030 if (status == MagickFalse)
1031 {
1032 image=DestroyImageList(image);
1033 return((Image *) NULL);
1034 }
1035 /*
1036 Initialize JPEG parameters.
1037 */
cristy91044972011-04-22 14:21:16 +00001038 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001039 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1040 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1041 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001042 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +00001043 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
1044 jpeg_pixels=(JSAMPLE *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001045 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001046 error_manager.image=image;
1047 if (setjmp(error_manager.error_recovery) != 0)
1048 {
1049 jpeg_destroy_decompress(&jpeg_info);
1050 (void) CloseBlob(image);
1051 number_pixels=(MagickSizeType) image->columns*image->rows;
1052 if (number_pixels != 0)
1053 return(GetFirstImageInList(image));
1054 return(DestroyImage(image));
1055 }
1056 jpeg_info.client_data=(void *) &error_manager;
1057 jpeg_create_decompress(&jpeg_info);
1058 JPEGSourceManager(&jpeg_info,image);
1059 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
1060 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
1061 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1062 for (i=1; i < 16; i++)
1063 if ((i != 2) && (i != 13) && (i != 14))
1064 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +00001065 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +00001066 if ((image_info->colorspace == YCbCrColorspace) ||
1067 (image_info->colorspace == Rec601YCbCrColorspace) ||
1068 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001069 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +00001070 if (IsITUFaxImage(image) != MagickFalse)
1071 {
1072 image->colorspace=LabColorspace;
1073 jpeg_info.out_color_space=JCS_YCbCr;
1074 }
1075 else
1076 if (jpeg_info.out_color_space == JCS_CMYK)
1077 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001078 /*
1079 Set image resolution.
1080 */
1081 units=0;
1082 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1083 (jpeg_info.Y_density != 1))
1084 {
cristy2a11bef2011-10-28 18:33:11 +00001085 image->resolution.x=(double) jpeg_info.X_density;
1086 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001087 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001088 }
1089 if (units == 1)
1090 image->units=PixelsPerInchResolution;
1091 if (units == 2)
1092 image->units=PixelsPerCentimeterResolution;
1093 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +00001094 option=GetImageOption(image_info,"jpeg:size");
1095 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001096 {
1097 double
1098 scale_factor;
1099
cristycaec74e2009-09-14 02:20:04 +00001100 GeometryInfo
1101 geometry_info;
1102
cristy0adb4f92009-11-28 18:08:51 +00001103 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001104 flags;
1105
cristy3ed852e2009-09-05 21:47:34 +00001106 /*
cristycaec74e2009-09-14 02:20:04 +00001107 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001108 */
cristy11151212009-09-14 13:10:15 +00001109 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001110 if ((flags & SigmaValue) == 0)
1111 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001112 jpeg_calc_output_dimensions(&jpeg_info);
1113 image->magick_columns=jpeg_info.output_width;
1114 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001115 scale_factor=1.0;
1116 if (geometry_info.rho != 0.0)
1117 scale_factor=jpeg_info.output_width/geometry_info.rho;
1118 if ((geometry_info.sigma != 0.0) &&
1119 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1120 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001121 jpeg_info.scale_num=1U;
1122 jpeg_info.scale_denom=(unsigned int) scale_factor;
1123 jpeg_calc_output_dimensions(&jpeg_info);
1124 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1126 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001127 }
cristybb503372010-05-27 20:51:26 +00001128 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001129#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1130#if defined(D_LOSSLESS_SUPPORTED)
1131 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1132 JPEGInterlace : NoInterlace;
1133 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1134 LosslessJPEGCompression : JPEGCompression;
1135 if (jpeg_info.data_precision > 8)
1136 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1137 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1138 image->filename);
1139 if (jpeg_info.data_precision == 16)
1140 jpeg_info.data_precision=12;
1141#else
1142 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1143 NoInterlace;
1144 image->compression=JPEGCompression;
1145#endif
1146#else
1147 image->compression=JPEGCompression;
1148 image->interlace=JPEGInterlace;
1149#endif
anthony41906552011-10-07 12:15:27 +00001150 option=GetImageOption(image_info,"jpeg:colors");
1151 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001152 {
cristyb4bb39c2012-02-21 18:45:54 +00001153 /*
1154 Let the JPEG library quantize the image.
1155 */
cristy3ed852e2009-09-05 21:47:34 +00001156 jpeg_info.quantize_colors=MagickTrue;
anthony41906552011-10-07 12:15:27 +00001157 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001158 }
cristy1b8d4462009-10-27 13:46:56 +00001159 option=GetImageOption(image_info,"jpeg:block-smoothing");
anthony6f201312012-03-30 04:08:15 +00001160 jpeg_info.do_block_smoothing=IsStringTrue(option);
cristyb4bb39c2012-02-21 18:45:54 +00001161 jpeg_info.dct_method=JDCT_FLOAT;
cristy787d4352010-03-06 13:55:58 +00001162 option=GetImageOption(image_info,"jpeg:dct-method");
1163 if (option != (const char *) NULL)
1164 switch (*option)
1165 {
1166 case 'D':
1167 case 'd':
1168 {
1169 if (LocaleCompare(option,"default") == 0)
1170 jpeg_info.dct_method=JDCT_DEFAULT;
1171 break;
1172 }
1173 case 'F':
1174 case 'f':
1175 {
1176 if (LocaleCompare(option,"fastest") == 0)
1177 jpeg_info.dct_method=JDCT_FASTEST;
1178 if (LocaleCompare(option,"float") == 0)
1179 jpeg_info.dct_method=JDCT_FLOAT;
1180 break;
1181 }
1182 case 'I':
1183 case 'i':
1184 {
1185 if (LocaleCompare(option,"ifast") == 0)
1186 jpeg_info.dct_method=JDCT_IFAST;
1187 if (LocaleCompare(option,"islow") == 0)
1188 jpeg_info.dct_method=JDCT_ISLOW;
1189 break;
1190 }
1191 }
cristy1b8d4462009-10-27 13:46:56 +00001192 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
anthony6f201312012-03-30 04:08:15 +00001193 jpeg_info.do_fancy_upsampling=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00001194 (void) jpeg_start_decompress(&jpeg_info);
1195 image->columns=jpeg_info.output_width;
1196 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001197 image->depth=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001198 if (jpeg_info.out_color_space == JCS_YCbCr)
1199 image->colorspace=YCbCrColorspace;
1200 if (jpeg_info.out_color_space == JCS_CMYK)
1201 image->colorspace=CMYKColorspace;
anthony41906552011-10-07 12:15:27 +00001202 option=GetImageOption(image_info,"jpeg:colors");
1203 if (option != (const char *) NULL)
1204 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1205 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001206 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1207 if ((jpeg_info.output_components == 1) &&
1208 (jpeg_info.quantize_colors == MagickFalse))
1209 {
cristybb503372010-05-27 20:51:26 +00001210 size_t
cristy3ed852e2009-09-05 21:47:34 +00001211 colors;
1212
cristybb503372010-05-27 20:51:26 +00001213 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001214 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001215 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1216 }
1217 if (image->debug != MagickFalse)
1218 {
1219 if (image->interlace != NoInterlace)
1220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1221 "Interlace: progressive");
1222 else
1223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1224 "Interlace: nonprogressive");
1225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1226 (int) jpeg_info.data_precision);
1227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1228 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1229 }
1230 JPEGSetImageQuality(&jpeg_info,image);
cristyd15e6592011-10-15 00:13:06 +00001231 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristyb51dff52011-05-19 16:55:47 +00001232 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001233 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001234 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001235 if (image_info->ping != MagickFalse)
1236 {
1237 jpeg_destroy_decompress(&jpeg_info);
1238 (void) CloseBlob(image);
1239 return(GetFirstImageInList(image));
1240 }
1241 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1242 jpeg_info.output_components*sizeof(JSAMPLE));
1243 if (jpeg_pixels == (JSAMPLE *) NULL)
1244 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1245 /*
1246 Convert JPEG pixels to pixel packets.
1247 */
1248 if (setjmp(error_manager.error_recovery) != 0)
1249 {
1250 if (jpeg_pixels != (unsigned char *) NULL)
1251 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1252 jpeg_destroy_decompress(&jpeg_info);
1253 (void) CloseBlob(image);
1254 number_pixels=(MagickSizeType) image->columns*image->rows;
1255 if (number_pixels != 0)
1256 return(GetFirstImageInList(image));
1257 return(DestroyImage(image));
1258 }
1259 if (jpeg_info.quantize_colors != MagickFalse)
1260 {
cristybb503372010-05-27 20:51:26 +00001261 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001262 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001263 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001264 {
cristy1b58f252012-03-01 01:41:41 +00001265 image->colormap[i].red=(double) ScaleCharToQuantum(
1266 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001267 image->colormap[i].green=image->colormap[i].red;
1268 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001269 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001270 }
1271 else
cristybb503372010-05-27 20:51:26 +00001272 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001273 {
cristy1b58f252012-03-01 01:41:41 +00001274 image->colormap[i].red=(double) ScaleCharToQuantum(
1275 jpeg_info.colormap[0][i]);
1276 image->colormap[i].green=(double) ScaleCharToQuantum(
1277 jpeg_info.colormap[1][i]);
1278 image->colormap[i].blue=(double) ScaleCharToQuantum(
1279 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001280 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001281 }
1282 }
1283 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001284 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001285 {
cristybb503372010-05-27 20:51:26 +00001286 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001287 x;
1288
cristy4c08aed2011-07-01 19:47:50 +00001289 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001290 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001291
1292 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1293 {
1294 (void) ThrowMagickException(exception,GetMagickModule(),
1295 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1296 continue;
1297 }
1298 p=jpeg_pixels;
1299 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001300 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001301 break;
cristy3ed852e2009-09-05 21:47:34 +00001302 if (jpeg_info.data_precision > 8)
1303 {
1304 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001305 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001306 {
cristybb503372010-05-27 20:51:26 +00001307 size_t
cristy3ed852e2009-09-05 21:47:34 +00001308 pixel;
1309
1310 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001311 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001312 else
cristybb503372010-05-27 20:51:26 +00001313 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristyc82a27b2011-10-21 01:07:16 +00001314 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001315 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001316 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001317 p++;
cristyed231572011-07-14 02:18:59 +00001318 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001319 }
1320 else
1321 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001322 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001323 {
cristy4c08aed2011-07-01 19:47:50 +00001324 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1325 (GETJSAMPLE(*p++) << 4)),q);
1326 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1327 (GETJSAMPLE(*p++) << 4)),q);
1328 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1329 (GETJSAMPLE(*p++) << 4)),q);
1330 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001331 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001332 }
1333 else
cristybb503372010-05-27 20:51:26 +00001334 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001335 {
cristy4c08aed2011-07-01 19:47:50 +00001336 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1337 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1338 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1339 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1340 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1341 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1342 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1343 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1344 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001345 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001346 }
1347 }
1348 else
1349 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001350 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001351 {
cristyc82a27b2011-10-21 01:07:16 +00001352 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001353 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001354 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001355 p++;
cristyed231572011-07-14 02:18:59 +00001356 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001357 }
1358 else
1359 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001360 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001361 {
cristy4c08aed2011-07-01 19:47:50 +00001362 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1363 GETJSAMPLE(*p++)),q);
1364 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1365 GETJSAMPLE(*p++)),q);
1366 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1367 GETJSAMPLE(*p++)),q);
1368 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001369 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001370 }
1371 else
cristybb503372010-05-27 20:51:26 +00001372 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001373 {
cristy4c08aed2011-07-01 19:47:50 +00001374 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1375 (unsigned char) GETJSAMPLE(*p++)),q);
1376 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1377 (unsigned char) GETJSAMPLE(*p++)),q);
1378 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1379 (unsigned char) GETJSAMPLE(*p++)),q);
1380 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1381 (unsigned char) GETJSAMPLE(*p++)),q);
1382 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001383 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001384 }
1385 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1386 break;
cristy524222d2011-04-25 00:37:06 +00001387 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1388 image->rows);
1389 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001390 {
1391 jpeg_abort_decompress(&jpeg_info);
1392 break;
1393 }
cristy3ed852e2009-09-05 21:47:34 +00001394 }
cristyd28b1dd2011-05-14 20:30:38 +00001395 if (status != MagickFalse)
1396 {
1397 error_manager.finished=MagickTrue;
1398 if (setjmp(error_manager.error_recovery) == 0)
1399 (void) jpeg_finish_decompress(&jpeg_info);
1400 }
cristy3ed852e2009-09-05 21:47:34 +00001401 /*
1402 Free jpeg resources.
1403 */
cristy3ed852e2009-09-05 21:47:34 +00001404 jpeg_destroy_decompress(&jpeg_info);
1405 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1406 (void) CloseBlob(image);
1407 return(GetFirstImageInList(image));
1408}
1409#endif
1410
1411/*
1412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1413% %
1414% %
1415% %
1416% R e g i s t e r J P E G I m a g e %
1417% %
1418% %
1419% %
1420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1421%
1422% RegisterJPEGImage() adds properties for the JPEG image format to
1423% the list of supported formats. The properties include the image format
1424% tag, a method to read and/or write the format, whether the format
1425% supports the saving of more than one frame to the same file or blob,
1426% whether the format supports native in-memory I/O, and a brief
1427% description of the format.
1428%
1429% The format of the RegisterJPEGImage method is:
1430%
cristybb503372010-05-27 20:51:26 +00001431% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001432%
1433*/
cristybb503372010-05-27 20:51:26 +00001434ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001435{
1436 char
1437 version[MaxTextExtent];
1438
1439 MagickInfo
1440 *entry;
1441
1442 static const char
cristy7138c592009-09-08 13:58:52 +00001443 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001444
1445 *version='\0';
1446#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001447 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001448#endif
1449 entry=SetMagickInfo("JPEG");
1450 entry->thread_support=NoThreadSupport;
1451#if defined(MAGICKCORE_JPEG_DELEGATE)
1452 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1453 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1454#endif
1455 entry->magick=(IsImageFormatHandler *) IsJPEG;
1456 entry->adjoin=MagickFalse;
1457 entry->description=ConstantString(description);
1458 if (*version != '\0')
1459 entry->version=ConstantString(version);
1460 entry->module=ConstantString("JPEG");
1461 (void) RegisterMagickInfo(entry);
1462 entry=SetMagickInfo("JPG");
1463 entry->thread_support=NoThreadSupport;
1464#if defined(MAGICKCORE_JPEG_DELEGATE)
1465 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1466 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1467#endif
1468 entry->adjoin=MagickFalse;
1469 entry->description=ConstantString(description);
1470 if (*version != '\0')
1471 entry->version=ConstantString(version);
1472 entry->module=ConstantString("JPEG");
1473 (void) RegisterMagickInfo(entry);
1474 entry=SetMagickInfo("PJPEG");
1475 entry->thread_support=NoThreadSupport;
1476#if defined(MAGICKCORE_JPEG_DELEGATE)
1477 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1478 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1479#endif
1480 entry->adjoin=MagickFalse;
1481 entry->description=ConstantString(description);
1482 if (*version != '\0')
1483 entry->version=ConstantString(version);
1484 entry->module=ConstantString("JPEG");
1485 (void) RegisterMagickInfo(entry);
1486 return(MagickImageCoderSignature);
1487}
1488
1489/*
1490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1491% %
1492% %
1493% %
1494% U n r e g i s t e r J P E G I m a g e %
1495% %
1496% %
1497% %
1498%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1499%
1500% UnregisterJPEGImage() removes format registrations made by the
1501% JPEG module from the list of supported formats.
1502%
1503% The format of the UnregisterJPEGImage method is:
1504%
1505% UnregisterJPEGImage(void)
1506%
1507*/
1508ModuleExport void UnregisterJPEGImage(void)
1509{
1510 (void) UnregisterMagickInfo("PJPG");
1511 (void) UnregisterMagickInfo("JPEG");
1512 (void) UnregisterMagickInfo("JPG");
1513}
1514
1515#if defined(MAGICKCORE_JPEG_DELEGATE)
1516/*
1517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518% %
1519% %
1520% %
1521% W r i t e J P E G I m a g e %
1522% %
1523% %
1524% %
1525%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1526%
1527% WriteJPEGImage() writes a JPEG image file and returns it. It
1528% allocates the memory necessary for the new Image structure and returns a
1529% pointer to the new image.
1530%
1531% The format of the WriteJPEGImage method is:
1532%
cristy91044972011-04-22 14:21:16 +00001533% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001534% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001535%
1536% A description of each parameter follows:
1537%
1538% o image_info: the image info.
1539%
1540% o jpeg_image: The image.
1541%
cristy1e178e72011-08-28 19:44:34 +00001542% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001543%
1544*/
1545
cristy1b58f252012-03-01 01:41:41 +00001546static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1547{
1548 assert(table != (QuantizationTable *) NULL);
1549 if (table->slot != (char *) NULL)
1550 table->slot=DestroyString(table->slot);
1551 if (table->description != (char *) NULL)
1552 table->description=DestroyString(table->description);
1553 if (table->levels != (unsigned int *) NULL)
1554 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1555 table=(QuantizationTable *) RelinquishMagickMemory(table);
1556 return(table);
1557}
1558
cristy3ed852e2009-09-05 21:47:34 +00001559static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1560{
1561 DestinationManager
1562 *destination;
1563
1564 destination=(DestinationManager *) cinfo->dest;
1565 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1566 MaxBufferExtent,destination->buffer);
1567 if (destination->manager.free_in_buffer != MaxBufferExtent)
1568 ERREXIT(cinfo,JERR_FILE_WRITE);
1569 destination->manager.next_output_byte=destination->buffer;
1570 return(TRUE);
1571}
1572
cristy1b58f252012-03-01 01:41:41 +00001573static QuantizationTable *GetQuantizationTable(const char *filename,
1574 const char *slot,ExceptionInfo *exception)
1575{
1576 char
1577 *p,
1578 *xml;
1579
1580 const char
1581 *attribute,
1582 *content;
1583
1584 double
1585 value;
1586
1587 register ssize_t
1588 i;
1589
1590 ssize_t
1591 j;
1592
1593 QuantizationTable
1594 *table;
1595
cristy1fcc8b62012-03-02 17:25:55 +00001596 size_t
1597 length;
1598
cristy1b58f252012-03-01 01:41:41 +00001599 XMLTreeInfo
1600 *description,
1601 *levels,
1602 *quantization_tables,
1603 *table_iterator;
1604
1605 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1606 "Loading quantization tables \"%s\" ...",filename);
1607 table=(QuantizationTable *) NULL;
1608 xml=FileToString(filename,~0,exception);
1609 if (xml == (char *) NULL)
1610 return(table);
1611 quantization_tables=NewXMLTree(xml,exception);
1612 if (quantization_tables == (XMLTreeInfo *) NULL)
1613 {
1614 xml=DestroyString(xml);
1615 return(table);
1616 }
1617 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1618 table_iterator != (XMLTreeInfo *) NULL;
1619 table_iterator=GetNextXMLTreeTag(table_iterator))
1620 {
1621 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1622 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1623 break;
1624 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1625 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1626 break;
1627 }
1628 if (table_iterator == (XMLTreeInfo *) NULL)
1629 {
1630 xml=DestroyString(xml);
1631 return(table);
1632 }
1633 description=GetXMLTreeChild(table_iterator,"description");
1634 if (description == (XMLTreeInfo *) NULL)
1635 {
1636 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1637 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1638 quantization_tables=DestroyXMLTree(quantization_tables);
1639 xml=DestroyString(xml);
1640 return(table);
1641 }
1642 levels=GetXMLTreeChild(table_iterator,"levels");
1643 if (levels == (XMLTreeInfo *) NULL)
1644 {
1645 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1646 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1647 quantization_tables=DestroyXMLTree(quantization_tables);
1648 xml=DestroyString(xml);
1649 return(table);
1650 }
1651 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1652 if (table == (QuantizationTable *) NULL)
1653 ThrowFatalException(ResourceLimitFatalError,
1654 "UnableToAcquireQuantizationTable");
1655 table->slot=(char *) NULL;
1656 table->description=(char *) NULL;
1657 table->levels=(unsigned int *) NULL;
1658 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1659 if (attribute != (char *) NULL)
1660 table->slot=ConstantString(attribute);
1661 content=GetXMLTreeContent(description);
1662 if (content != (char *) NULL)
1663 table->description=ConstantString(content);
1664 attribute=GetXMLTreeAttribute(levels,"width");
1665 if (attribute == (char *) NULL)
1666 {
1667 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1668 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1669 quantization_tables=DestroyXMLTree(quantization_tables);
1670 table=DestroyQuantizationTable(table);
1671 xml=DestroyString(xml);
1672 return(table);
1673 }
1674 table->width=StringToUnsignedLong(attribute);
1675 if (table->width == 0)
1676 {
1677 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1678 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1679 quantization_tables=DestroyXMLTree(quantization_tables);
1680 table=DestroyQuantizationTable(table);
1681 xml=DestroyString(xml);
1682 return(table);
1683 }
1684 attribute=GetXMLTreeAttribute(levels,"height");
1685 if (attribute == (char *) NULL)
1686 {
1687 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1688 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1689 quantization_tables=DestroyXMLTree(quantization_tables);
1690 table=DestroyQuantizationTable(table);
1691 xml=DestroyString(xml);
1692 return(table);
1693 }
1694 table->height=StringToUnsignedLong(attribute);
1695 if (table->height == 0)
1696 {
1697 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1698 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1699 quantization_tables=DestroyXMLTree(quantization_tables);
1700 table=DestroyQuantizationTable(table);
1701 xml=DestroyString(xml);
1702 return(table);
1703 }
1704 attribute=GetXMLTreeAttribute(levels,"divisor");
1705 if (attribute == (char *) NULL)
1706 {
1707 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1708 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1709 quantization_tables=DestroyXMLTree(quantization_tables);
1710 table=DestroyQuantizationTable(table);
1711 xml=DestroyString(xml);
1712 return(table);
1713 }
cristy043f3f32012-03-02 17:37:28 +00001714 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1715 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001716 {
1717 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1718 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1719 quantization_tables=DestroyXMLTree(quantization_tables);
1720 table=DestroyQuantizationTable(table);
1721 xml=DestroyString(xml);
1722 return(table);
1723 }
1724 content=GetXMLTreeContent(levels);
1725 if (content == (char *) NULL)
1726 {
1727 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1728 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1729 quantization_tables=DestroyXMLTree(quantization_tables);
1730 table=DestroyQuantizationTable(table);
1731 xml=DestroyString(xml);
1732 return(table);
1733 }
cristy1fcc8b62012-03-02 17:25:55 +00001734 length=(size_t) table->width*table->height;
1735 if (length < 64)
1736 length=64;
cristy092006b2012-03-02 17:26:02 +00001737 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001738 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001739 if (table->levels == (unsigned int *) NULL)
1740 ThrowFatalException(ResourceLimitFatalError,
1741 "UnableToAcquireQuantizationTable");
1742 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1743 {
cristy043f3f32012-03-02 17:37:28 +00001744 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1745 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001746 while (isspace((int) ((unsigned char) *p)) != 0)
1747 p++;
1748 if (*p == ',')
1749 p++;
1750 content=p;
1751 }
cristy043f3f32012-03-02 17:37:28 +00001752 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001753 (void) value;
1754 if (p != content)
1755 {
1756 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1757 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1758 quantization_tables=DestroyXMLTree(quantization_tables);
1759 table=DestroyQuantizationTable(table);
1760 xml=DestroyString(xml);
1761 return(table);
1762 }
cristy043f3f32012-03-02 17:37:28 +00001763 for (j=i; j < 64; j++)
1764 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001765 quantization_tables=DestroyXMLTree(quantization_tables);
1766 xml=DestroyString(xml);
1767 return(table);
1768}
1769
cristy3ed852e2009-09-05 21:47:34 +00001770static void InitializeDestination(j_compress_ptr cinfo)
1771{
1772 DestinationManager
1773 *destination;
1774
1775 destination=(DestinationManager *) cinfo->dest;
1776 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1777 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1778 destination->manager.next_output_byte=destination->buffer;
1779 destination->manager.free_in_buffer=MaxBufferExtent;
1780}
1781
1782static inline size_t MagickMin(const size_t x,const size_t y)
1783{
1784 if (x < y)
1785 return(x);
1786 return(y);
1787}
1788
1789static void TerminateDestination(j_compress_ptr cinfo)
1790{
1791 DestinationManager
1792 *destination;
1793
1794 destination=(DestinationManager *) cinfo->dest;
1795 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1796 {
1797 ssize_t
1798 count;
1799
1800 count=WriteBlob(destination->image,MaxBufferExtent-
1801 destination->manager.free_in_buffer,destination->buffer);
1802 if (count != (ssize_t)
1803 (MaxBufferExtent-destination->manager.free_in_buffer))
1804 ERREXIT(cinfo,JERR_FILE_WRITE);
1805 }
1806}
1807
1808static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1809{
1810 const char
1811 *name;
1812
1813 const StringInfo
1814 *profile;
1815
1816 MagickBooleanType
1817 iptc;
1818
cristybb503372010-05-27 20:51:26 +00001819 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001820 i;
1821
1822 size_t
cristy524222d2011-04-25 00:37:06 +00001823 length,
1824 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001825
1826 StringInfo
1827 *custom_profile;
1828
cristy3ed852e2009-09-05 21:47:34 +00001829 /*
1830 Save image profile as a APP marker.
1831 */
1832 iptc=MagickFalse;
1833 custom_profile=AcquireStringInfo(65535L);
1834 ResetImageProfileIterator(image);
1835 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1836 {
cristy109e5572010-09-16 18:38:17 +00001837 register unsigned char
1838 *p;
1839
cristy3ed852e2009-09-05 21:47:34 +00001840 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001841 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001842 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001843 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001844 {
1845 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1846 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1847 (unsigned int) length);
1848 }
1849 if (LocaleCompare(name,"ICC") == 0)
1850 {
1851 register unsigned char
1852 *p;
1853
1854 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001855 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001856 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001857 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001858 {
1859 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001860 p[12]=(unsigned char) ((i/65519L)+1);
1861 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1862 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1863 length);
1864 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1865 custom_profile),(unsigned int) (length+tag_length));
1866 }
1867 }
1868 if (((LocaleCompare(name,"IPTC") == 0) ||
1869 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1870 {
cristybb503372010-05-27 20:51:26 +00001871 size_t
cristy3ed852e2009-09-05 21:47:34 +00001872 roundup;
1873
1874 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001875 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001876 {
1877 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001878 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001879 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1880 {
1881 (void) memcpy(p,"Photoshop 3.0 ",14);
1882 tag_length=14;
1883 }
1884 else
1885 {
1886 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1887 tag_length=26;
1888 p[24]=(unsigned char) (length >> 8);
1889 p[25]=(unsigned char) (length & 0xff);
1890 }
1891 p[13]=0x00;
1892 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001893 if (roundup != 0)
1894 p[length+tag_length]='\0';
1895 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1896 custom_profile),(unsigned int) (length+tag_length+roundup));
1897 }
1898 }
1899 if (LocaleCompare(name,"XMP") == 0)
1900 {
1901 StringInfo
1902 *xmp_profile;
1903
1904 /*
1905 Add namespace to XMP profile.
1906 */
cristy0615f0e2011-10-12 11:36:46 +00001907 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy3ed852e2009-09-05 21:47:34 +00001908 ConcatenateStringInfo(xmp_profile,profile);
1909 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001910 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001911 {
1912 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1913 jpeg_write_marker(jpeg_info,XML_MARKER,
1914 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1915 }
1916 xmp_profile=DestroyStringInfo(xmp_profile);
1917 }
cristye8c25f92010-06-03 00:53:06 +00001918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1919 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001920 name=GetNextImageProfile(image);
1921 }
1922 custom_profile=DestroyStringInfo(custom_profile);
1923}
1924
1925static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1926{
1927 DestinationManager
1928 *destination;
1929
1930 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1931 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1932 destination=(DestinationManager *) cinfo->dest;
1933 destination->manager.init_destination=InitializeDestination;
1934 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1935 destination->manager.term_destination=TerminateDestination;
1936 destination->image=image;
1937}
1938
1939static char **SamplingFactorToList(const char *text)
1940{
1941 char
1942 **textlist;
1943
1944 register char
1945 *q;
1946
1947 register const char
1948 *p;
1949
cristybb503372010-05-27 20:51:26 +00001950 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001951 i;
1952
cristybb503372010-05-27 20:51:26 +00001953 size_t
cristy3ed852e2009-09-05 21:47:34 +00001954 lines;
1955
1956 if (text == (char *) NULL)
1957 return((char **) NULL);
1958 /*
1959 Convert string to an ASCII list.
1960 */
1961 lines=1;
1962 for (p=text; *p != '\0'; p++)
1963 if (*p == ',')
1964 lines++;
1965 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1966 sizeof(*textlist));
1967 if (textlist == (char **) NULL)
1968 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1969 p=text;
cristybb503372010-05-27 20:51:26 +00001970 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001971 {
1972 for (q=(char *) p; *q != '\0'; q++)
1973 if (*q == ',')
1974 break;
1975 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1976 sizeof(*textlist[i]));
1977 if (textlist[i] == (char *) NULL)
1978 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1979 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1980 if (*q == '\r')
1981 q++;
1982 p=q+1;
1983 }
1984 textlist[i]=(char *) NULL;
1985 return(textlist);
1986}
1987
1988static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001989 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001990{
1991 const char
1992 *option,
1993 *sampling_factor,
1994 *value;
1995
1996 ErrorManager
1997 error_manager;
1998
cristy21749e92012-02-18 02:29:21 +00001999 int
2000 quality;
2001
cristy3ed852e2009-09-05 21:47:34 +00002002 JSAMPLE
2003 *jpeg_pixels;
2004
2005 JSAMPROW
2006 scanline[1];
2007
cristy3ed852e2009-09-05 21:47:34 +00002008 MagickBooleanType
2009 status;
2010
2011 register JSAMPLE
2012 *q;
2013
cristybb503372010-05-27 20:51:26 +00002014 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002015 i;
2016
cristy524222d2011-04-25 00:37:06 +00002017 ssize_t
2018 y;
2019
cristy3ed852e2009-09-05 21:47:34 +00002020 struct jpeg_compress_struct
2021 jpeg_info;
2022
2023 struct jpeg_error_mgr
2024 jpeg_error;
2025
2026 /*
2027 Open image file.
2028 */
2029 assert(image_info != (const ImageInfo *) NULL);
2030 assert(image_info->signature == MagickSignature);
2031 assert(image != (Image *) NULL);
2032 assert(image->signature == MagickSignature);
2033 if (image->debug != MagickFalse)
2034 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002035 assert(exception != (ExceptionInfo *) NULL);
2036 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00002037 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002038 if (status == MagickFalse)
2039 return(status);
2040 /*
2041 Initialize JPEG parameters.
2042 */
cristy91044972011-04-22 14:21:16 +00002043 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002044 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2045 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2046 jpeg_info.client_data=(void *) image;
2047 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002048 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002049 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002050 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002051 error_manager.image=image;
2052 jpeg_pixels=(JSAMPLE *) NULL;
2053 if (setjmp(error_manager.error_recovery) != 0)
2054 {
2055 jpeg_destroy_compress(&jpeg_info);
2056 (void) CloseBlob(image);
2057 return(MagickFalse);
2058 }
2059 jpeg_info.client_data=(void *) &error_manager;
2060 jpeg_create_compress(&jpeg_info);
2061 JPEGDestinationManager(&jpeg_info,image);
2062 if ((image->columns != (unsigned int) image->columns) ||
2063 (image->rows != (unsigned int) image->rows))
2064 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2065 jpeg_info.image_width=(unsigned int) image->columns;
2066 jpeg_info.image_height=(unsigned int) image->rows;
2067 jpeg_info.input_components=3;
2068 jpeg_info.data_precision=8;
2069 jpeg_info.in_color_space=JCS_RGB;
2070 switch (image->colorspace)
2071 {
2072 case CMYKColorspace:
2073 {
2074 jpeg_info.input_components=4;
2075 jpeg_info.in_color_space=JCS_CMYK;
2076 break;
2077 }
2078 case YCbCrColorspace:
2079 case Rec601YCbCrColorspace:
2080 case Rec709YCbCrColorspace:
2081 {
2082 jpeg_info.in_color_space=JCS_YCbCr;
2083 break;
2084 }
2085 case GRAYColorspace:
2086 case Rec601LumaColorspace:
2087 case Rec709LumaColorspace:
2088 {
2089 jpeg_info.input_components=1;
2090 jpeg_info.in_color_space=JCS_GRAYSCALE;
2091 break;
2092 }
2093 default:
cristy9f396782009-12-21 01:37:45 +00002094 {
cristy510d06a2011-07-06 23:43:54 +00002095 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +00002096 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00002097 break;
cristy9f396782009-12-21 01:37:45 +00002098 }
cristy3ed852e2009-09-05 21:47:34 +00002099 }
2100 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00002101 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002102 {
2103 jpeg_info.input_components=1;
2104 jpeg_info.in_color_space=JCS_GRAYSCALE;
2105 }
2106 jpeg_set_defaults(&jpeg_info);
2107 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2108 jpeg_info.data_precision=8;
2109 else
2110 if (sizeof(JSAMPLE) > 1)
2111 jpeg_info.data_precision=12;
2112 jpeg_info.density_unit=(UINT8) 1;
2113 if (image->debug != MagickFalse)
2114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002115 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2116 floor(image->resolution.y+0.5));
2117 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002118 {
2119 /*
2120 Set image resolution.
2121 */
2122 jpeg_info.write_JFIF_header=MagickTrue;
cristy2a11bef2011-10-28 18:33:11 +00002123 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2124 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002125 if (image->units == PixelsPerInchResolution)
2126 jpeg_info.density_unit=(UINT8) 1;
2127 if (image->units == PixelsPerCentimeterResolution)
2128 jpeg_info.density_unit=(UINT8) 2;
2129 }
cristy97cb3bf2012-02-21 18:31:20 +00002130 jpeg_info.dct_method=JDCT_FLOAT;
cristy3ed852e2009-09-05 21:47:34 +00002131 option=GetImageOption(image_info,"jpeg:dct-method");
2132 if (option != (const char *) NULL)
2133 switch (*option)
2134 {
2135 case 'D':
2136 case 'd':
2137 {
2138 if (LocaleCompare(option,"default") == 0)
2139 jpeg_info.dct_method=JDCT_DEFAULT;
2140 break;
2141 }
2142 case 'F':
2143 case 'f':
2144 {
2145 if (LocaleCompare(option,"fastest") == 0)
2146 jpeg_info.dct_method=JDCT_FASTEST;
2147 if (LocaleCompare(option,"float") == 0)
2148 jpeg_info.dct_method=JDCT_FLOAT;
2149 break;
2150 }
2151 case 'I':
2152 case 'i':
2153 {
2154 if (LocaleCompare(option,"ifast") == 0)
2155 jpeg_info.dct_method=JDCT_IFAST;
2156 if (LocaleCompare(option,"islow") == 0)
2157 jpeg_info.dct_method=JDCT_ISLOW;
2158 break;
2159 }
2160 }
2161 option=GetImageOption(image_info,"jpeg:optimize-coding");
2162 if (option != (const char *) NULL)
anthony6f201312012-03-30 04:08:15 +00002163 jpeg_info.optimize_coding=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00002164 else
2165 {
2166 MagickSizeType
2167 length;
2168
2169 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2170 image->rows*sizeof(JSAMPLE);
2171 if (length == (MagickSizeType) ((size_t) length))
2172 {
2173 /*
2174 Perform optimization only if available memory resources permit it.
2175 */
2176 status=AcquireMagickResource(MemoryResource,length);
cristy3ed852e2009-09-05 21:47:34 +00002177 RelinquishMagickResource(MemoryResource,length);
anthony6f201312012-03-30 04:08:15 +00002178 jpeg_info.optimize_coding=status;
cristy3ed852e2009-09-05 21:47:34 +00002179 }
2180 }
2181#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2182 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2183 (image_info->interlace != NoInterlace))
2184 {
2185 if (image->debug != MagickFalse)
2186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2187 "Interlace: progressive");
2188 jpeg_simple_progression(&jpeg_info);
2189 }
2190 else
2191 if (image->debug != MagickFalse)
2192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2193 "Interlace: non-progressive");
2194#else
2195 if (image->debug != MagickFalse)
2196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2197 "Interlace: nonprogressive");
2198#endif
cristy0adb4f92009-11-28 18:08:51 +00002199 option=GetImageOption(image_info,"jpeg:extent");
2200 if (option != (const char *) NULL)
2201 {
2202 Image
2203 *jpeg_image;
2204
2205 ImageInfo
2206 *jpeg_info;
2207
2208 jpeg_info=CloneImageInfo(image_info);
cristy1e178e72011-08-28 19:44:34 +00002209 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002210 if (jpeg_image != (Image *) NULL)
2211 {
2212 MagickSizeType
2213 extent;
2214
2215 size_t
cristy87e73ab2010-02-12 01:59:12 +00002216 maximum,
2217 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002218
2219 /*
2220 Search for compression quality that does not exceed image extent.
2221 */
2222 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002223 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002224 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
2225 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00002226 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00002227 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002228 {
cristy87e73ab2010-02-12 01:59:12 +00002229 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy1e178e72011-08-28 19:44:34 +00002230 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002231 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00002232 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002233 else
cristy87e73ab2010-02-12 01:59:12 +00002234 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00002235 }
2236 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00002237 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002238 jpeg_image=DestroyImage(jpeg_image);
2239 }
2240 jpeg_info=DestroyImageInfo(jpeg_info);
2241 }
cristy21749e92012-02-18 02:29:21 +00002242 quality=92;
cristy3ed852e2009-09-05 21:47:34 +00002243 if ((image_info->compression != LosslessJPEGCompression) &&
2244 (image->quality <= 100))
2245 {
cristy21749e92012-02-18 02:29:21 +00002246 if (image->quality != UndefinedCompressionQuality)
2247 quality=(int) image->quality;
cristy3ed852e2009-09-05 21:47:34 +00002248 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00002249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2250 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00002251 }
2252 else
2253 {
2254#if !defined(C_LOSSLESS_SUPPORTED)
cristy21749e92012-02-18 02:29:21 +00002255 quality=100;
cristy3ed852e2009-09-05 21:47:34 +00002256 if (image->debug != MagickFalse)
2257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2258#else
2259 if (image->quality < 100)
cristy1e178e72011-08-28 19:44:34 +00002260 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2261 "LosslessToLossyJPEGConversion",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002262 else
2263 {
2264 int
2265 point_transform,
2266 predictor;
2267
2268 predictor=image->quality/100; /* range 1-7 */
2269 point_transform=image->quality % 20; /* range 0-15 */
2270 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2271 if (image->debug != MagickFalse)
2272 {
2273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2274 "Compression: lossless");
2275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2276 "Predictor: %d",predictor);
2277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2278 "Point Transform: %d",point_transform);
2279 }
2280 }
2281#endif
2282 }
cristy21749e92012-02-18 02:29:21 +00002283 jpeg_set_quality(&jpeg_info,quality,MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00002284 sampling_factor=(const char *) NULL;
cristyd15e6592011-10-15 00:13:06 +00002285 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
cristy3ed852e2009-09-05 21:47:34 +00002286 if (value != (char *) NULL)
2287 {
2288 sampling_factor=value;
2289 if (image->debug != MagickFalse)
2290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2291 " Input sampling-factors=%s",sampling_factor);
2292 }
2293 if (image_info->sampling_factor != (char *) NULL)
2294 sampling_factor=image_info->sampling_factor;
2295 if (sampling_factor == (const char *) NULL)
2296 {
2297 if (image->quality >= 90)
2298 for (i=0; i < MAX_COMPONENTS; i++)
2299 {
2300 jpeg_info.comp_info[i].h_samp_factor=1;
2301 jpeg_info.comp_info[i].v_samp_factor=1;
2302 }
2303 }
2304 else
2305 {
2306 char
2307 **factors;
2308
2309 GeometryInfo
2310 geometry_info;
2311
2312 MagickStatusType
2313 flags;
2314
2315 /*
2316 Set sampling factor.
2317 */
2318 i=0;
2319 factors=SamplingFactorToList(sampling_factor);
2320 if (factors != (char **) NULL)
2321 {
2322 for (i=0; i < MAX_COMPONENTS; i++)
2323 {
2324 if (factors[i] == (char *) NULL)
2325 break;
2326 flags=ParseGeometry(factors[i],&geometry_info);
2327 if ((flags & SigmaValue) == 0)
2328 geometry_info.sigma=geometry_info.rho;
2329 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2330 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2331 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2332 }
2333 factors=(char **) RelinquishMagickMemory(factors);
2334 }
2335 for ( ; i < MAX_COMPONENTS; i++)
2336 {
2337 jpeg_info.comp_info[i].h_samp_factor=1;
2338 jpeg_info.comp_info[i].v_samp_factor=1;
2339 }
2340 }
2341 if (jpeg_info.input_components == 1)
2342 for (i=0; i < MAX_COMPONENTS; i++)
2343 {
2344 jpeg_info.comp_info[i].h_samp_factor=1;
2345 jpeg_info.comp_info[i].v_samp_factor=1;
2346 }
cristy1b58f252012-03-01 01:41:41 +00002347 option=GetImageOption(image_info,"jpeg:q-table");
2348 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002349 {
cristy1b58f252012-03-01 01:41:41 +00002350 QuantizationTable
2351 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002352
cristy1445b322012-02-19 18:57:30 +00002353 /*
cristy1b58f252012-03-01 01:41:41 +00002354 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002355 */
cristy1b58f252012-03-01 01:41:41 +00002356 table=GetQuantizationTable(option,"0",exception);
2357 if (table != (QuantizationTable *) NULL)
2358 {
2359 jpeg_add_quant_table(&jpeg_info,0,table->levels,jpeg_quality_scaling(
2360 quality),0);
2361 table=DestroyQuantizationTable(table);
2362 }
2363 table=GetQuantizationTable(option,"1",exception);
2364 if (table != (QuantizationTable *) NULL)
2365 {
2366 jpeg_add_quant_table(&jpeg_info,1,table->levels,jpeg_quality_scaling(
2367 quality),0);
2368 table=DestroyQuantizationTable(table);
2369 }
2370 table=GetQuantizationTable(option,"2",exception);
2371 if (table != (QuantizationTable *) NULL)
2372 {
2373 jpeg_add_quant_table(&jpeg_info,2,table->levels,jpeg_quality_scaling(
2374 quality),0);
2375 table=DestroyQuantizationTable(table);
2376 }
2377 table=GetQuantizationTable(option,"3",exception);
2378 if (table != (QuantizationTable *) NULL)
2379 {
2380 jpeg_add_quant_table(&jpeg_info,3,table->levels,jpeg_quality_scaling(
2381 quality),0);
2382 table=DestroyQuantizationTable(table);
2383 }
cristy1445b322012-02-19 18:57:30 +00002384 }
cristy3ed852e2009-09-05 21:47:34 +00002385 jpeg_start_compress(&jpeg_info,MagickTrue);
2386 if (image->debug != MagickFalse)
2387 {
2388 if (image->storage_class == PseudoClass)
2389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2390 "Storage class: PseudoClass");
2391 else
2392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2393 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2395 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002396 if (image->colors != 0)
2397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002398 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002399 else
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 "Number of colors: unspecified");
2402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2403 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2404 switch (image->colorspace)
2405 {
2406 case CMYKColorspace:
2407 {
2408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2409 "Storage class: DirectClass");
2410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2411 "Colorspace: CMYK");
2412 break;
2413 }
2414 case YCbCrColorspace:
2415 case Rec601YCbCrColorspace:
2416 case Rec709YCbCrColorspace:
2417 {
2418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2419 "Colorspace: YCbCr");
2420 break;
2421 }
2422 default:
2423 break;
2424 }
2425 switch (image->colorspace)
2426 {
2427 case CMYKColorspace:
2428 {
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2430 "Colorspace: CMYK");
2431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2432 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2433 jpeg_info.comp_info[0].h_samp_factor,
2434 jpeg_info.comp_info[0].v_samp_factor,
2435 jpeg_info.comp_info[1].h_samp_factor,
2436 jpeg_info.comp_info[1].v_samp_factor,
2437 jpeg_info.comp_info[2].h_samp_factor,
2438 jpeg_info.comp_info[2].v_samp_factor,
2439 jpeg_info.comp_info[3].h_samp_factor,
2440 jpeg_info.comp_info[3].v_samp_factor);
2441 break;
2442 }
2443 case GRAYColorspace:
2444 case Rec601LumaColorspace:
2445 case Rec709LumaColorspace:
2446 {
2447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2448 "Colorspace: GRAY");
2449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2450 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2451 jpeg_info.comp_info[0].v_samp_factor);
2452 break;
2453 }
2454 case RGBColorspace:
2455 {
2456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2457 "Image colorspace is RGB");
2458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2459 "Sampling factors: %dx%d,%dx%d,%dx%d",
2460 jpeg_info.comp_info[0].h_samp_factor,
2461 jpeg_info.comp_info[0].v_samp_factor,
2462 jpeg_info.comp_info[1].h_samp_factor,
2463 jpeg_info.comp_info[1].v_samp_factor,
2464 jpeg_info.comp_info[2].h_samp_factor,
2465 jpeg_info.comp_info[2].v_samp_factor);
2466 break;
2467 }
2468 case YCbCrColorspace:
2469 case Rec601YCbCrColorspace:
2470 case Rec709YCbCrColorspace:
2471 {
2472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2473 "Colorspace: YCbCr");
2474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2475 "Sampling factors: %dx%d,%dx%d,%dx%d",
2476 jpeg_info.comp_info[0].h_samp_factor,
2477 jpeg_info.comp_info[0].v_samp_factor,
2478 jpeg_info.comp_info[1].h_samp_factor,
2479 jpeg_info.comp_info[1].v_samp_factor,
2480 jpeg_info.comp_info[2].h_samp_factor,
2481 jpeg_info.comp_info[2].v_samp_factor);
2482 break;
2483 }
2484 default:
2485 {
2486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2487 image->colorspace);
2488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2489 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2490 jpeg_info.comp_info[0].h_samp_factor,
2491 jpeg_info.comp_info[0].v_samp_factor,
2492 jpeg_info.comp_info[1].h_samp_factor,
2493 jpeg_info.comp_info[1].v_samp_factor,
2494 jpeg_info.comp_info[2].h_samp_factor,
2495 jpeg_info.comp_info[2].v_samp_factor,
2496 jpeg_info.comp_info[3].h_samp_factor,
2497 jpeg_info.comp_info[3].v_samp_factor);
2498 break;
2499 }
2500 }
2501 }
2502 /*
2503 Write JPEG profiles.
2504 */
cristyd15e6592011-10-15 00:13:06 +00002505 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002506 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002507 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002508 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2509 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2510 if (image->profiles != (void *) NULL)
2511 WriteProfile(&jpeg_info,image);
2512 /*
2513 Convert MIFF to JPEG raster pixels.
2514 */
2515 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2516 jpeg_info.input_components*sizeof(*jpeg_pixels));
2517 if (jpeg_pixels == (JSAMPLE *) NULL)
2518 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2519 if (setjmp(error_manager.error_recovery) != 0)
2520 {
2521 jpeg_destroy_compress(&jpeg_info);
2522 if (jpeg_pixels != (unsigned char *) NULL)
2523 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2524 (void) CloseBlob(image);
2525 return(MagickFalse);
2526 }
2527 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002528 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002529 {
cristy3ed852e2009-09-05 21:47:34 +00002530 if ((jpeg_info.in_color_space == JCS_RGB) ||
2531 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002532 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002533 {
cristy4c08aed2011-07-01 19:47:50 +00002534 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002535 *p;
2536
cristybb503372010-05-27 20:51:26 +00002537 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002538 x;
2539
cristy1e178e72011-08-28 19:44:34 +00002540 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002541 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002542 break;
2543 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002544 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002545 {
cristy4c08aed2011-07-01 19:47:50 +00002546 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2547 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2548 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002549 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002550 }
2551 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002552 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2553 image->rows);
2554 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002555 break;
2556 }
2557 else
cristye90d7402010-03-14 18:21:29 +00002558 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002559 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002560 {
cristy4c08aed2011-07-01 19:47:50 +00002561 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002562 *p;
2563
cristybb503372010-05-27 20:51:26 +00002564 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002565 x;
2566
cristy1e178e72011-08-28 19:44:34 +00002567 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002568 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002569 break;
2570 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002571 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002572 {
cristy4c08aed2011-07-01 19:47:50 +00002573 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristyed231572011-07-14 02:18:59 +00002574 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002575 }
2576 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002577 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2578 image->rows);
2579 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002580 break;
2581 }
2582 else
cristybb503372010-05-27 20:51:26 +00002583 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002584 {
cristy4c08aed2011-07-01 19:47:50 +00002585 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002586 *p;
2587
cristybb503372010-05-27 20:51:26 +00002588 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002589 x;
2590
cristy1e178e72011-08-28 19:44:34 +00002591 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002592 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002593 break;
2594 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002595 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002596 {
2597 /*
2598 Convert DirectClass packets to contiguous CMYK scanlines.
2599 */
2600 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002601 GetPixelRed(image,p))));
cristye90d7402010-03-14 18:21:29 +00002602 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002603 GetPixelGreen(image,p))));
cristye90d7402010-03-14 18:21:29 +00002604 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002605 GetPixelBlue(image,p))));
cristye90d7402010-03-14 18:21:29 +00002606 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002607 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002608 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002609 }
2610 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002611 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2612 image->rows);
2613 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002614 break;
2615 }
2616 }
2617 else
2618 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002619 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002620 {
cristy4c08aed2011-07-01 19:47:50 +00002621 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002622 *p;
2623
cristybb503372010-05-27 20:51:26 +00002624 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002625 x;
2626
cristy1e178e72011-08-28 19:44:34 +00002627 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002628 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002629 break;
2630 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002631 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002632 {
cristy1fcc8b62012-03-02 17:25:55 +00002633 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002634 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002635 }
2636 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002637 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2638 image->rows);
2639 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002640 break;
2641 }
2642 else
2643 if ((jpeg_info.in_color_space == JCS_RGB) ||
2644 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002645 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002646 {
cristy4c08aed2011-07-01 19:47:50 +00002647 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002648 *p;
2649
cristybb503372010-05-27 20:51:26 +00002650 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002651 x;
2652
cristy1e178e72011-08-28 19:44:34 +00002653 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002654 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002655 break;
2656 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002657 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002658 {
cristy4c08aed2011-07-01 19:47:50 +00002659 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2660 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2661 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002662 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002663 }
2664 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002665 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002666 image->rows);
cristye90d7402010-03-14 18:21:29 +00002667 if (status == MagickFalse)
2668 break;
2669 }
2670 else
cristybb503372010-05-27 20:51:26 +00002671 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002672 {
cristy4c08aed2011-07-01 19:47:50 +00002673 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002674 *p;
2675
cristybb503372010-05-27 20:51:26 +00002676 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002677 x;
2678
cristy1e178e72011-08-28 19:44:34 +00002679 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002680 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002681 break;
2682 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002683 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002684 {
2685 /*
2686 Convert DirectClass packets to contiguous CMYK scanlines.
2687 */
cristye90d7402010-03-14 18:21:29 +00002688 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002689 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002690 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002691 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002692 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002693 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002694 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002695 GetPixelBlack(image,p)) >> 4));
cristyed231572011-07-14 02:18:59 +00002696 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002697 }
2698 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002699 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002700 image->rows);
cristye90d7402010-03-14 18:21:29 +00002701 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002702 break;
2703 }
cristybb503372010-05-27 20:51:26 +00002704 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002705 jpeg_finish_compress(&jpeg_info);
2706 /*
2707 Relinquish resources.
2708 */
2709 jpeg_destroy_compress(&jpeg_info);
2710 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2711 (void) CloseBlob(image);
2712 return(MagickTrue);
2713}
2714#endif