blob: db7b066e1375612024cb69ec4bd341adf9c35351 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% JJJJJ PPPP EEEEE GGGG %
7% J P P E G %
8% J PPPP EEE G GG %
9% J J P E G G %
10% JJJ P EEEEE GGG %
11% %
12% %
13% Read/Write JPEG Image Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36% This software is based in part on the work of the Independent JPEG Group.
37% See ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz for copyright and
38% licensing restrictions. Blob support contributed by Glenn Randers-Pehrson.
39%
40%
41*/
42
43/*
44 Include declarations.
45*/
cristy4c08aed2011-07-01 19:47:50 +000046#include "MagickCore/studio.h"
47#include "MagickCore/attribute.h"
48#include "MagickCore/blob.h"
49#include "MagickCore/blob-private.h"
50#include "MagickCore/cache.h"
51#include "MagickCore/color.h"
52#include "MagickCore/colormap-private.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colormap.h"
55#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000056#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000057#include "MagickCore/constitute.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/magick.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/module.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/profile.h"
73#include "MagickCore/property.h"
74#include "MagickCore/quantum-private.h"
75#include "MagickCore/resource_.h"
76#include "MagickCore/splay-tree.h"
77#include "MagickCore/static.h"
78#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
cristye40005d2012-03-23 12:18:45 +000080#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000081#include "MagickCore/utility.h"
cristy1b58f252012-03-01 01:41:41 +000082#include "MagickCore/xml-tree.h"
83#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000084#include <setjmp.h>
85#if defined(MAGICKCORE_JPEG_DELEGATE)
86#define JPEG_INTERNAL_OPTIONS
87#if defined(__MINGW32__)
88# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
cristye7e40552010-04-24 21:34:22 +000089typedef unsigned char boolean;
cristyc6da28e2011-04-28 01:41:35 +000090#define HAVE_BOOLEAN
cristy3ed852e2009-09-05 21:47:34 +000091#endif
92#undef HAVE_STDLIB_H
93#include "jpeglib.h"
94#include "jerror.h"
95#endif
96
97/*
98 Define declarations.
99*/
100#define ICC_MARKER (JPEG_APP0+2)
101#define ICC_PROFILE "ICC_PROFILE"
102#define IPTC_MARKER (JPEG_APP0+13)
103#define XML_MARKER (JPEG_APP0+1)
cristyeb9759e2012-06-07 23:06:29 +0000104#define MaxBufferExtent 16384
cristy3ed852e2009-09-05 21:47:34 +0000105
106/*
107 Typedef declarations.
108*/
109#if defined(MAGICKCORE_JPEG_DELEGATE)
110typedef struct _DestinationManager
111{
112 struct jpeg_destination_mgr
113 manager;
114
115 Image
116 *image;
117
118 JOCTET
119 *buffer;
120} DestinationManager;
121
122typedef struct _ErrorManager
123{
cristy018f07f2011-09-04 21:15:19 +0000124 ExceptionInfo
125 *exception;
126
cristy3ed852e2009-09-05 21:47:34 +0000127 Image
128 *image;
129
cristyd28b1dd2011-05-14 20:30:38 +0000130 MagickBooleanType
131 finished;
132
cristy3ed852e2009-09-05 21:47:34 +0000133 jmp_buf
134 error_recovery;
135} ErrorManager;
136
137typedef struct _SourceManager
138{
139 struct jpeg_source_mgr
140 manager;
141
142 Image
143 *image;
144
145 JOCTET
146 *buffer;
147
148 boolean
149 start_of_blob;
150} SourceManager;
151#endif
cristy1b58f252012-03-01 01:41:41 +0000152
153typedef struct _QuantizationTable
154{
155 char
156 *slot,
157 *description;
158
159 size_t
160 width,
161 height;
162
cristy043f3f32012-03-02 17:37:28 +0000163 double
164 divisor;
165
cristy1b58f252012-03-01 01:41:41 +0000166 unsigned int
cristy1b58f252012-03-01 01:41:41 +0000167 *levels;
168} QuantizationTable;
cristy3ed852e2009-09-05 21:47:34 +0000169
170/*
171 Forward declarations.
172*/
173#if defined(MAGICKCORE_JPEG_DELEGATE)
174static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000175 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000176#endif
177
178/*
179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180% %
181% %
182% %
183% I s J P E G %
184% %
185% %
186% %
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188%
189% IsJPEG() returns MagickTrue if the image format type, identified by the
190% magick string, is JPEG.
191%
192% The format of the IsJPEG method is:
193%
194% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
195%
196% A description of each parameter follows:
197%
198% o magick: compare image format pattern against these bytes.
199%
200% o length: Specifies the length of the magick string.
201%
202*/
203static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
204{
205 if (length < 3)
206 return(MagickFalse);
207 if (memcmp(magick,"\377\330\377",3) == 0)
208 return(MagickTrue);
209 return(MagickFalse);
210}
211
212#if defined(MAGICKCORE_JPEG_DELEGATE)
213/*
214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215% %
216% %
217% %
218% R e a d J P E G I m a g e %
219% %
220% %
221% %
222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223%
224% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
225% the memory necessary for the new Image structure and returns a pointer to
226% the new image.
227%
228% The format of the ReadJPEGImage method is:
229%
230% Image *ReadJPEGImage(const ImageInfo *image_info,
231% ExceptionInfo *exception)
232%
233% A description of each parameter follows:
234%
235% o image_info: the image info.
236%
237% o exception: return any errors or warnings in this structure.
238%
239*/
240
cristy3ed852e2009-09-05 21:47:34 +0000241static boolean FillInputBuffer(j_decompress_ptr cinfo)
242{
243 SourceManager
244 *source;
245
246 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000247 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
248 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000249 if (source->manager.bytes_in_buffer == 0)
250 {
cristy453b8202012-06-07 22:57:52 +0000251 if (source->start_of_blob != FALSE)
cristy3ed852e2009-09-05 21:47:34 +0000252 ERREXIT(cinfo,JERR_INPUT_EMPTY);
253 WARNMS(cinfo,JWRN_JPEG_EOF);
254 source->buffer[0]=(JOCTET) 0xff;
255 source->buffer[1]=(JOCTET) JPEG_EOI;
256 source->manager.bytes_in_buffer=2;
257 }
258 source->manager.next_input_byte=source->buffer;
259 source->start_of_blob=FALSE;
260 return(TRUE);
261}
262
263static int GetCharacter(j_decompress_ptr jpeg_info)
264{
265 if (jpeg_info->src->bytes_in_buffer == 0)
266 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
267 jpeg_info->src->bytes_in_buffer--;
268 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
269}
270
271static void InitializeSource(j_decompress_ptr cinfo)
272{
273 SourceManager
274 *source;
275
276 source=(SourceManager *) cinfo->src;
277 source->start_of_blob=TRUE;
278}
279
cristye8dd1302009-11-11 02:45:03 +0000280static MagickBooleanType IsITUFaxImage(const Image *image)
281{
282 const StringInfo
283 *profile;
284
285 const unsigned char
286 *datum;
287
cristyace6aa42009-11-11 03:17:33 +0000288 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000289 if (profile == (const StringInfo *) NULL)
290 return(MagickFalse);
291 if (GetStringInfoLength(profile) < 5)
292 return(MagickFalse);
293 datum=GetStringInfoDatum(profile);
294 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
295 (datum[3] == 0x41) && (datum[4] == 0x58))
296 return(MagickTrue);
297 return(MagickFalse);
298}
299
cristy437c3932011-10-12 18:03:46 +0000300static void JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000301{
cristyd28b1dd2011-05-14 20:30:38 +0000302 char
303 message[JMSG_LENGTH_MAX];
304
cristy3ed852e2009-09-05 21:47:34 +0000305 ErrorManager
306 *error_manager;
307
cristyc82a27b2011-10-21 01:07:16 +0000308 ExceptionInfo
309 *exception;
310
cristyd28b1dd2011-05-14 20:30:38 +0000311 Image
312 *image;
313
314 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000315 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000316 image=error_manager->image;
cristyc82a27b2011-10-21 01:07:16 +0000317 exception=error_manager->exception;
cristy86f33542011-05-21 22:58:33 +0000318 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000319 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
321 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000322 if (error_manager->finished != MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000323 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
324 (char *) message,"`%s'",image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000325 else
cristyc82a27b2011-10-21 01:07:16 +0000326 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
327 (char *) message,"`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000328 longjmp(error_manager->error_recovery,1);
329}
330
cristyd28b1dd2011-05-14 20:30:38 +0000331static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
332{
cristyf162e012012-03-28 12:54:33 +0000333#define JPEGExcessiveWarnings 1000
334
cristyd28b1dd2011-05-14 20:30:38 +0000335 char
336 message[JMSG_LENGTH_MAX];
337
338 ErrorManager
339 *error_manager;
340
cristy018f07f2011-09-04 21:15:19 +0000341 ExceptionInfo
342 *exception;
343
cristyd28b1dd2011-05-14 20:30:38 +0000344 Image
345 *image;
346
347 *message='\0';
348 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000349 exception=error_manager->exception;
cristyd28b1dd2011-05-14 20:30:38 +0000350 image=error_manager->image;
351 if (level < 0)
352 {
353 /*
354 Process warning message.
355 */
356 (jpeg_info->err->format_message)(jpeg_info,message);
cristyf162e012012-03-28 12:54:33 +0000357 if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
358 JPEGErrorHandler(jpeg_info);
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;
1070 /*
1071 Set image resolution.
1072 */
1073 units=0;
1074 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1075 (jpeg_info.Y_density != 1))
1076 {
cristy2a11bef2011-10-28 18:33:11 +00001077 image->resolution.x=(double) jpeg_info.X_density;
1078 image->resolution.y=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001079 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001080 }
1081 if (units == 1)
1082 image->units=PixelsPerInchResolution;
1083 if (units == 2)
1084 image->units=PixelsPerCentimeterResolution;
1085 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +00001086 option=GetImageOption(image_info,"jpeg:size");
1087 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001088 {
1089 double
1090 scale_factor;
1091
cristycaec74e2009-09-14 02:20:04 +00001092 GeometryInfo
1093 geometry_info;
1094
cristy0adb4f92009-11-28 18:08:51 +00001095 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001096 flags;
1097
cristy3ed852e2009-09-05 21:47:34 +00001098 /*
cristycaec74e2009-09-14 02:20:04 +00001099 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001100 */
cristy11151212009-09-14 13:10:15 +00001101 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001102 if ((flags & SigmaValue) == 0)
1103 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001104 jpeg_calc_output_dimensions(&jpeg_info);
1105 image->magick_columns=jpeg_info.output_width;
1106 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001107 scale_factor=1.0;
1108 if (geometry_info.rho != 0.0)
1109 scale_factor=jpeg_info.output_width/geometry_info.rho;
1110 if ((geometry_info.sigma != 0.0) &&
1111 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1112 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001113 jpeg_info.scale_num=1U;
1114 jpeg_info.scale_denom=(unsigned int) scale_factor;
1115 jpeg_calc_output_dimensions(&jpeg_info);
1116 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1118 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001119 }
cristybb503372010-05-27 20:51:26 +00001120 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001121#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1122#if defined(D_LOSSLESS_SUPPORTED)
1123 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1124 JPEGInterlace : NoInterlace;
1125 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1126 LosslessJPEGCompression : JPEGCompression;
1127 if (jpeg_info.data_precision > 8)
1128 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1129 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1130 image->filename);
1131 if (jpeg_info.data_precision == 16)
1132 jpeg_info.data_precision=12;
1133#else
1134 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1135 NoInterlace;
1136 image->compression=JPEGCompression;
1137#endif
1138#else
1139 image->compression=JPEGCompression;
1140 image->interlace=JPEGInterlace;
1141#endif
anthony41906552011-10-07 12:15:27 +00001142 option=GetImageOption(image_info,"jpeg:colors");
1143 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001144 {
cristyb4bb39c2012-02-21 18:45:54 +00001145 /*
1146 Let the JPEG library quantize the image.
1147 */
cristy3ed852e2009-09-05 21:47:34 +00001148 jpeg_info.quantize_colors=MagickTrue;
anthony41906552011-10-07 12:15:27 +00001149 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001150 }
cristy1b8d4462009-10-27 13:46:56 +00001151 option=GetImageOption(image_info,"jpeg:block-smoothing");
anthony6f201312012-03-30 04:08:15 +00001152 jpeg_info.do_block_smoothing=IsStringTrue(option);
cristyb4bb39c2012-02-21 18:45:54 +00001153 jpeg_info.dct_method=JDCT_FLOAT;
cristy787d4352010-03-06 13:55:58 +00001154 option=GetImageOption(image_info,"jpeg:dct-method");
1155 if (option != (const char *) NULL)
1156 switch (*option)
1157 {
1158 case 'D':
1159 case 'd':
1160 {
1161 if (LocaleCompare(option,"default") == 0)
1162 jpeg_info.dct_method=JDCT_DEFAULT;
1163 break;
1164 }
1165 case 'F':
1166 case 'f':
1167 {
1168 if (LocaleCompare(option,"fastest") == 0)
1169 jpeg_info.dct_method=JDCT_FASTEST;
1170 if (LocaleCompare(option,"float") == 0)
1171 jpeg_info.dct_method=JDCT_FLOAT;
1172 break;
1173 }
1174 case 'I':
1175 case 'i':
1176 {
1177 if (LocaleCompare(option,"ifast") == 0)
1178 jpeg_info.dct_method=JDCT_IFAST;
1179 if (LocaleCompare(option,"islow") == 0)
1180 jpeg_info.dct_method=JDCT_ISLOW;
1181 break;
1182 }
1183 }
cristy1b8d4462009-10-27 13:46:56 +00001184 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
anthony6f201312012-03-30 04:08:15 +00001185 jpeg_info.do_fancy_upsampling=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00001186 (void) jpeg_start_decompress(&jpeg_info);
1187 image->columns=jpeg_info.output_width;
1188 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001189 image->depth=(size_t) jpeg_info.data_precision;
cristy93ff2cc2012-05-13 21:03:53 +00001190 switch (jpeg_info.out_color_space)
1191 {
1192 case JCS_RGB:
1193 default:
1194 {
1195 SetImageColorspace(image,sRGBColorspace,exception);
1196 break;
1197 }
1198 case JCS_GRAYSCALE:
1199 {
1200 SetImageColorspace(image,GRAYColorspace,exception);
1201 break;
1202 }
1203 case JCS_YCbCr:
1204 {
1205 SetImageColorspace(image,YCbCrColorspace,exception);
1206 break;
1207 }
1208 case JCS_CMYK:
1209 {
1210 SetImageColorspace(image,CMYKColorspace,exception);
1211 break;
1212 }
1213 }
cristy36fc5502012-05-22 11:32:23 +00001214 if (IsITUFaxImage(image) != MagickFalse)
1215 {
1216 SetImageColorspace(image,LabColorspace,exception);
1217 jpeg_info.out_color_space=JCS_YCbCr;
1218 }
anthony41906552011-10-07 12:15:27 +00001219 option=GetImageOption(image_info,"jpeg:colors");
1220 if (option != (const char *) NULL)
1221 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1222 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001223 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1224 if ((jpeg_info.output_components == 1) &&
1225 (jpeg_info.quantize_colors == MagickFalse))
1226 {
cristybb503372010-05-27 20:51:26 +00001227 size_t
cristy3ed852e2009-09-05 21:47:34 +00001228 colors;
1229
cristybb503372010-05-27 20:51:26 +00001230 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001231 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001232 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1233 }
1234 if (image->debug != MagickFalse)
1235 {
1236 if (image->interlace != NoInterlace)
1237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1238 "Interlace: progressive");
1239 else
1240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1241 "Interlace: nonprogressive");
1242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1243 (int) jpeg_info.data_precision);
1244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1245 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1246 }
1247 JPEGSetImageQuality(&jpeg_info,image);
cristyd15e6592011-10-15 00:13:06 +00001248 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristyb51dff52011-05-19 16:55:47 +00001249 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001250 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001251 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001252 if (image_info->ping != MagickFalse)
1253 {
1254 jpeg_destroy_decompress(&jpeg_info);
1255 (void) CloseBlob(image);
1256 return(GetFirstImageInList(image));
1257 }
1258 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1259 jpeg_info.output_components*sizeof(JSAMPLE));
1260 if (jpeg_pixels == (JSAMPLE *) NULL)
1261 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1262 /*
1263 Convert JPEG pixels to pixel packets.
1264 */
1265 if (setjmp(error_manager.error_recovery) != 0)
1266 {
1267 if (jpeg_pixels != (unsigned char *) NULL)
1268 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1269 jpeg_destroy_decompress(&jpeg_info);
1270 (void) CloseBlob(image);
1271 number_pixels=(MagickSizeType) image->columns*image->rows;
1272 if (number_pixels != 0)
1273 return(GetFirstImageInList(image));
1274 return(DestroyImage(image));
1275 }
1276 if (jpeg_info.quantize_colors != MagickFalse)
1277 {
cristybb503372010-05-27 20:51:26 +00001278 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001279 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001280 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001281 {
cristy1b58f252012-03-01 01:41:41 +00001282 image->colormap[i].red=(double) ScaleCharToQuantum(
1283 jpeg_info.colormap[0][i]);
cristy3ed852e2009-09-05 21:47:34 +00001284 image->colormap[i].green=image->colormap[i].red;
1285 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001286 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001287 }
1288 else
cristybb503372010-05-27 20:51:26 +00001289 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001290 {
cristy1b58f252012-03-01 01:41:41 +00001291 image->colormap[i].red=(double) ScaleCharToQuantum(
1292 jpeg_info.colormap[0][i]);
1293 image->colormap[i].green=(double) ScaleCharToQuantum(
1294 jpeg_info.colormap[1][i]);
1295 image->colormap[i].blue=(double) ScaleCharToQuantum(
1296 jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001297 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001298 }
1299 }
1300 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001301 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001302 {
cristybb503372010-05-27 20:51:26 +00001303 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001304 x;
1305
cristy4c08aed2011-07-01 19:47:50 +00001306 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001307 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001308
1309 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1310 {
1311 (void) ThrowMagickException(exception,GetMagickModule(),
1312 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1313 continue;
1314 }
1315 p=jpeg_pixels;
1316 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001317 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001318 break;
cristy3ed852e2009-09-05 21:47:34 +00001319 if (jpeg_info.data_precision > 8)
1320 {
1321 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001322 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001323 {
cristybb503372010-05-27 20:51:26 +00001324 size_t
cristy3ed852e2009-09-05 21:47:34 +00001325 pixel;
1326
1327 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001328 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001329 else
cristybb503372010-05-27 20:51:26 +00001330 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristyc82a27b2011-10-21 01:07:16 +00001331 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001332 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001333 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001334 p++;
cristyed231572011-07-14 02:18:59 +00001335 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001336 }
1337 else
1338 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001339 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001340 {
cristy4c08aed2011-07-01 19:47:50 +00001341 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1342 (GETJSAMPLE(*p++) << 4)),q);
1343 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1344 (GETJSAMPLE(*p++) << 4)),q);
1345 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1346 (GETJSAMPLE(*p++) << 4)),q);
1347 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001348 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001349 }
1350 else
cristybb503372010-05-27 20:51:26 +00001351 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001352 {
cristy4c08aed2011-07-01 19:47:50 +00001353 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1354 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1355 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1356 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1357 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1358 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1359 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1360 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1361 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001362 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001363 }
1364 }
1365 else
1366 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001367 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001368 {
cristyc82a27b2011-10-21 01:07:16 +00001369 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001370 SetPixelIndex(image,index,q);
cristy803640d2011-11-17 02:11:32 +00001371 SetPixelInfoPixel(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001372 p++;
cristyed231572011-07-14 02:18:59 +00001373 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001374 }
1375 else
1376 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001377 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001378 {
cristy4c08aed2011-07-01 19:47:50 +00001379 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1380 GETJSAMPLE(*p++)),q);
1381 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1382 GETJSAMPLE(*p++)),q);
1383 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1384 GETJSAMPLE(*p++)),q);
1385 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001386 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001387 }
1388 else
cristybb503372010-05-27 20:51:26 +00001389 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001390 {
cristy4c08aed2011-07-01 19:47:50 +00001391 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1392 (unsigned char) GETJSAMPLE(*p++)),q);
1393 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1394 (unsigned char) GETJSAMPLE(*p++)),q);
1395 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1396 (unsigned char) GETJSAMPLE(*p++)),q);
1397 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1398 (unsigned char) GETJSAMPLE(*p++)),q);
1399 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001400 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001401 }
1402 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1403 break;
cristy524222d2011-04-25 00:37:06 +00001404 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1405 image->rows);
1406 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001407 {
1408 jpeg_abort_decompress(&jpeg_info);
1409 break;
1410 }
cristy3ed852e2009-09-05 21:47:34 +00001411 }
cristyd28b1dd2011-05-14 20:30:38 +00001412 if (status != MagickFalse)
1413 {
1414 error_manager.finished=MagickTrue;
1415 if (setjmp(error_manager.error_recovery) == 0)
1416 (void) jpeg_finish_decompress(&jpeg_info);
1417 }
cristy3ed852e2009-09-05 21:47:34 +00001418 /*
1419 Free jpeg resources.
1420 */
cristy3ed852e2009-09-05 21:47:34 +00001421 jpeg_destroy_decompress(&jpeg_info);
1422 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1423 (void) CloseBlob(image);
1424 return(GetFirstImageInList(image));
1425}
1426#endif
1427
1428/*
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430% %
1431% %
1432% %
1433% R e g i s t e r J P E G I m a g e %
1434% %
1435% %
1436% %
1437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438%
1439% RegisterJPEGImage() adds properties for the JPEG image format to
1440% the list of supported formats. The properties include the image format
1441% tag, a method to read and/or write the format, whether the format
1442% supports the saving of more than one frame to the same file or blob,
1443% whether the format supports native in-memory I/O, and a brief
1444% description of the format.
1445%
1446% The format of the RegisterJPEGImage method is:
1447%
cristybb503372010-05-27 20:51:26 +00001448% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001449%
1450*/
cristybb503372010-05-27 20:51:26 +00001451ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001452{
1453 char
1454 version[MaxTextExtent];
1455
1456 MagickInfo
1457 *entry;
1458
1459 static const char
cristy7138c592009-09-08 13:58:52 +00001460 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001461
1462 *version='\0';
1463#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001464 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001465#endif
1466 entry=SetMagickInfo("JPEG");
1467 entry->thread_support=NoThreadSupport;
1468#if defined(MAGICKCORE_JPEG_DELEGATE)
1469 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1470 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1471#endif
1472 entry->magick=(IsImageFormatHandler *) IsJPEG;
1473 entry->adjoin=MagickFalse;
1474 entry->description=ConstantString(description);
1475 if (*version != '\0')
1476 entry->version=ConstantString(version);
1477 entry->module=ConstantString("JPEG");
1478 (void) RegisterMagickInfo(entry);
1479 entry=SetMagickInfo("JPG");
1480 entry->thread_support=NoThreadSupport;
1481#if defined(MAGICKCORE_JPEG_DELEGATE)
1482 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1483 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1484#endif
1485 entry->adjoin=MagickFalse;
1486 entry->description=ConstantString(description);
1487 if (*version != '\0')
1488 entry->version=ConstantString(version);
1489 entry->module=ConstantString("JPEG");
1490 (void) RegisterMagickInfo(entry);
1491 entry=SetMagickInfo("PJPEG");
1492 entry->thread_support=NoThreadSupport;
1493#if defined(MAGICKCORE_JPEG_DELEGATE)
1494 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1495 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1496#endif
1497 entry->adjoin=MagickFalse;
1498 entry->description=ConstantString(description);
1499 if (*version != '\0')
1500 entry->version=ConstantString(version);
1501 entry->module=ConstantString("JPEG");
1502 (void) RegisterMagickInfo(entry);
1503 return(MagickImageCoderSignature);
1504}
1505
1506/*
1507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1508% %
1509% %
1510% %
1511% U n r e g i s t e r J P E G I m a g e %
1512% %
1513% %
1514% %
1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1516%
1517% UnregisterJPEGImage() removes format registrations made by the
1518% JPEG module from the list of supported formats.
1519%
1520% The format of the UnregisterJPEGImage method is:
1521%
1522% UnregisterJPEGImage(void)
1523%
1524*/
1525ModuleExport void UnregisterJPEGImage(void)
1526{
1527 (void) UnregisterMagickInfo("PJPG");
1528 (void) UnregisterMagickInfo("JPEG");
1529 (void) UnregisterMagickInfo("JPG");
1530}
1531
1532#if defined(MAGICKCORE_JPEG_DELEGATE)
1533/*
1534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1535% %
1536% %
1537% %
1538% W r i t e J P E G I m a g e %
1539% %
1540% %
1541% %
1542%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1543%
1544% WriteJPEGImage() writes a JPEG image file and returns it. It
1545% allocates the memory necessary for the new Image structure and returns a
1546% pointer to the new image.
1547%
1548% The format of the WriteJPEGImage method is:
1549%
cristy91044972011-04-22 14:21:16 +00001550% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001551% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001552%
1553% A description of each parameter follows:
1554%
1555% o image_info: the image info.
1556%
1557% o jpeg_image: The image.
1558%
cristy1e178e72011-08-28 19:44:34 +00001559% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001560%
1561*/
1562
cristy1b58f252012-03-01 01:41:41 +00001563static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1564{
1565 assert(table != (QuantizationTable *) NULL);
1566 if (table->slot != (char *) NULL)
1567 table->slot=DestroyString(table->slot);
1568 if (table->description != (char *) NULL)
1569 table->description=DestroyString(table->description);
1570 if (table->levels != (unsigned int *) NULL)
1571 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1572 table=(QuantizationTable *) RelinquishMagickMemory(table);
1573 return(table);
1574}
1575
cristy3ed852e2009-09-05 21:47:34 +00001576static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1577{
1578 DestinationManager
1579 *destination;
1580
1581 destination=(DestinationManager *) cinfo->dest;
1582 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1583 MaxBufferExtent,destination->buffer);
1584 if (destination->manager.free_in_buffer != MaxBufferExtent)
1585 ERREXIT(cinfo,JERR_FILE_WRITE);
1586 destination->manager.next_output_byte=destination->buffer;
1587 return(TRUE);
1588}
1589
cristy1b58f252012-03-01 01:41:41 +00001590static QuantizationTable *GetQuantizationTable(const char *filename,
1591 const char *slot,ExceptionInfo *exception)
1592{
1593 char
1594 *p,
1595 *xml;
1596
1597 const char
1598 *attribute,
1599 *content;
1600
1601 double
1602 value;
1603
1604 register ssize_t
1605 i;
1606
1607 ssize_t
1608 j;
1609
1610 QuantizationTable
1611 *table;
1612
cristy1fcc8b62012-03-02 17:25:55 +00001613 size_t
1614 length;
1615
cristy1b58f252012-03-01 01:41:41 +00001616 XMLTreeInfo
1617 *description,
1618 *levels,
1619 *quantization_tables,
1620 *table_iterator;
1621
1622 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1623 "Loading quantization tables \"%s\" ...",filename);
1624 table=(QuantizationTable *) NULL;
1625 xml=FileToString(filename,~0,exception);
1626 if (xml == (char *) NULL)
1627 return(table);
1628 quantization_tables=NewXMLTree(xml,exception);
1629 if (quantization_tables == (XMLTreeInfo *) NULL)
1630 {
1631 xml=DestroyString(xml);
1632 return(table);
1633 }
1634 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1635 table_iterator != (XMLTreeInfo *) NULL;
1636 table_iterator=GetNextXMLTreeTag(table_iterator))
1637 {
1638 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1639 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1640 break;
1641 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1642 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1643 break;
1644 }
1645 if (table_iterator == (XMLTreeInfo *) NULL)
1646 {
1647 xml=DestroyString(xml);
1648 return(table);
1649 }
1650 description=GetXMLTreeChild(table_iterator,"description");
1651 if (description == (XMLTreeInfo *) NULL)
1652 {
1653 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1654 "XmlMissingElement", "<description>, slot \"%s\"",slot);
1655 quantization_tables=DestroyXMLTree(quantization_tables);
1656 xml=DestroyString(xml);
1657 return(table);
1658 }
1659 levels=GetXMLTreeChild(table_iterator,"levels");
1660 if (levels == (XMLTreeInfo *) NULL)
1661 {
1662 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1663 "XmlMissingElement", "<levels>, slot \"%s\"", slot);
1664 quantization_tables=DestroyXMLTree(quantization_tables);
1665 xml=DestroyString(xml);
1666 return(table);
1667 }
1668 table=(QuantizationTable *) AcquireMagickMemory(sizeof(*table));
1669 if (table == (QuantizationTable *) NULL)
1670 ThrowFatalException(ResourceLimitFatalError,
1671 "UnableToAcquireQuantizationTable");
1672 table->slot=(char *) NULL;
1673 table->description=(char *) NULL;
1674 table->levels=(unsigned int *) NULL;
1675 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1676 if (attribute != (char *) NULL)
1677 table->slot=ConstantString(attribute);
1678 content=GetXMLTreeContent(description);
1679 if (content != (char *) NULL)
1680 table->description=ConstantString(content);
1681 attribute=GetXMLTreeAttribute(levels,"width");
1682 if (attribute == (char *) NULL)
1683 {
1684 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1685 "XmlMissingAttribute", "<levels width>, slot \"%s\"",slot);
1686 quantization_tables=DestroyXMLTree(quantization_tables);
1687 table=DestroyQuantizationTable(table);
1688 xml=DestroyString(xml);
1689 return(table);
1690 }
1691 table->width=StringToUnsignedLong(attribute);
1692 if (table->width == 0)
1693 {
1694 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1695 "XmlInvalidAttribute", "<levels width>, table \"%s\"",slot);
1696 quantization_tables=DestroyXMLTree(quantization_tables);
1697 table=DestroyQuantizationTable(table);
1698 xml=DestroyString(xml);
1699 return(table);
1700 }
1701 attribute=GetXMLTreeAttribute(levels,"height");
1702 if (attribute == (char *) NULL)
1703 {
1704 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1705 "XmlMissingAttribute", "<levels height>, table \"%s\"",slot);
1706 quantization_tables=DestroyXMLTree(quantization_tables);
1707 table=DestroyQuantizationTable(table);
1708 xml=DestroyString(xml);
1709 return(table);
1710 }
1711 table->height=StringToUnsignedLong(attribute);
1712 if (table->height == 0)
1713 {
1714 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1715 "XmlInvalidAttribute", "<levels height>, table \"%s\"",slot);
1716 quantization_tables=DestroyXMLTree(quantization_tables);
1717 table=DestroyQuantizationTable(table);
1718 xml=DestroyString(xml);
1719 return(table);
1720 }
1721 attribute=GetXMLTreeAttribute(levels,"divisor");
1722 if (attribute == (char *) NULL)
1723 {
1724 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1725 "XmlMissingAttribute", "<levels divisor>, table \"%s\"",slot);
1726 quantization_tables=DestroyXMLTree(quantization_tables);
1727 table=DestroyQuantizationTable(table);
1728 xml=DestroyString(xml);
1729 return(table);
1730 }
cristy043f3f32012-03-02 17:37:28 +00001731 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1732 if (table->divisor == 0.0)
cristy1b58f252012-03-01 01:41:41 +00001733 {
1734 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1735 "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
1736 quantization_tables=DestroyXMLTree(quantization_tables);
1737 table=DestroyQuantizationTable(table);
1738 xml=DestroyString(xml);
1739 return(table);
1740 }
1741 content=GetXMLTreeContent(levels);
1742 if (content == (char *) NULL)
1743 {
1744 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1745 "XmlMissingContent", "<levels>, table \"%s\"",slot);
1746 quantization_tables=DestroyXMLTree(quantization_tables);
1747 table=DestroyQuantizationTable(table);
1748 xml=DestroyString(xml);
1749 return(table);
1750 }
cristy1fcc8b62012-03-02 17:25:55 +00001751 length=(size_t) table->width*table->height;
1752 if (length < 64)
1753 length=64;
cristy092006b2012-03-02 17:26:02 +00001754 table->levels=(unsigned int *) AcquireQuantumMemory(length,
cristy1fcc8b62012-03-02 17:25:55 +00001755 sizeof(*table->levels));
cristy1b58f252012-03-01 01:41:41 +00001756 if (table->levels == (unsigned int *) NULL)
1757 ThrowFatalException(ResourceLimitFatalError,
1758 "UnableToAcquireQuantizationTable");
1759 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1760 {
cristy043f3f32012-03-02 17:37:28 +00001761 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1762 table->divisor+0.5);
cristy1b58f252012-03-01 01:41:41 +00001763 while (isspace((int) ((unsigned char) *p)) != 0)
1764 p++;
1765 if (*p == ',')
1766 p++;
1767 content=p;
1768 }
cristy043f3f32012-03-02 17:37:28 +00001769 value=InterpretLocaleValue(content,&p);
cristy1b58f252012-03-01 01:41:41 +00001770 (void) value;
1771 if (p != content)
1772 {
1773 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1774 "XmlInvalidContent", "<level> too many values, table \"%s\"",slot);
1775 quantization_tables=DestroyXMLTree(quantization_tables);
1776 table=DestroyQuantizationTable(table);
1777 xml=DestroyString(xml);
1778 return(table);
1779 }
cristy043f3f32012-03-02 17:37:28 +00001780 for (j=i; j < 64; j++)
1781 table->levels[j]=table->levels[j-1];
cristy1b58f252012-03-01 01:41:41 +00001782 quantization_tables=DestroyXMLTree(quantization_tables);
1783 xml=DestroyString(xml);
1784 return(table);
1785}
1786
cristy3ed852e2009-09-05 21:47:34 +00001787static void InitializeDestination(j_compress_ptr cinfo)
1788{
1789 DestinationManager
1790 *destination;
1791
1792 destination=(DestinationManager *) cinfo->dest;
1793 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1794 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1795 destination->manager.next_output_byte=destination->buffer;
1796 destination->manager.free_in_buffer=MaxBufferExtent;
1797}
1798
1799static inline size_t MagickMin(const size_t x,const size_t y)
1800{
1801 if (x < y)
1802 return(x);
1803 return(y);
1804}
1805
1806static void TerminateDestination(j_compress_ptr cinfo)
1807{
1808 DestinationManager
1809 *destination;
1810
1811 destination=(DestinationManager *) cinfo->dest;
1812 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1813 {
1814 ssize_t
1815 count;
1816
1817 count=WriteBlob(destination->image,MaxBufferExtent-
1818 destination->manager.free_in_buffer,destination->buffer);
1819 if (count != (ssize_t)
1820 (MaxBufferExtent-destination->manager.free_in_buffer))
1821 ERREXIT(cinfo,JERR_FILE_WRITE);
1822 }
1823}
1824
1825static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1826{
1827 const char
1828 *name;
1829
1830 const StringInfo
1831 *profile;
1832
1833 MagickBooleanType
1834 iptc;
1835
cristybb503372010-05-27 20:51:26 +00001836 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001837 i;
1838
1839 size_t
cristy524222d2011-04-25 00:37:06 +00001840 length,
1841 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001842
1843 StringInfo
1844 *custom_profile;
1845
cristy3ed852e2009-09-05 21:47:34 +00001846 /*
1847 Save image profile as a APP marker.
1848 */
1849 iptc=MagickFalse;
1850 custom_profile=AcquireStringInfo(65535L);
1851 ResetImageProfileIterator(image);
1852 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1853 {
cristy109e5572010-09-16 18:38:17 +00001854 register unsigned char
1855 *p;
1856
cristy3ed852e2009-09-05 21:47:34 +00001857 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001858 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001859 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001860 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001861 {
1862 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1863 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1864 (unsigned int) length);
1865 }
1866 if (LocaleCompare(name,"ICC") == 0)
1867 {
1868 register unsigned char
1869 *p;
1870
1871 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001872 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001873 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001874 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001875 {
1876 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001877 p[12]=(unsigned char) ((i/65519L)+1);
1878 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1879 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1880 length);
1881 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1882 custom_profile),(unsigned int) (length+tag_length));
1883 }
1884 }
1885 if (((LocaleCompare(name,"IPTC") == 0) ||
1886 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1887 {
cristybb503372010-05-27 20:51:26 +00001888 size_t
cristy3ed852e2009-09-05 21:47:34 +00001889 roundup;
1890
1891 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001892 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001893 {
1894 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001895 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001896 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1897 {
1898 (void) memcpy(p,"Photoshop 3.0 ",14);
1899 tag_length=14;
1900 }
1901 else
1902 {
1903 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1904 tag_length=26;
1905 p[24]=(unsigned char) (length >> 8);
1906 p[25]=(unsigned char) (length & 0xff);
1907 }
1908 p[13]=0x00;
1909 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001910 if (roundup != 0)
1911 p[length+tag_length]='\0';
1912 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1913 custom_profile),(unsigned int) (length+tag_length+roundup));
1914 }
1915 }
1916 if (LocaleCompare(name,"XMP") == 0)
1917 {
1918 StringInfo
1919 *xmp_profile;
1920
1921 /*
1922 Add namespace to XMP profile.
1923 */
cristy0615f0e2011-10-12 11:36:46 +00001924 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy3ed852e2009-09-05 21:47:34 +00001925 ConcatenateStringInfo(xmp_profile,profile);
1926 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001927 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001928 {
1929 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1930 jpeg_write_marker(jpeg_info,XML_MARKER,
1931 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1932 }
1933 xmp_profile=DestroyStringInfo(xmp_profile);
1934 }
cristye8c25f92010-06-03 00:53:06 +00001935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1936 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001937 name=GetNextImageProfile(image);
1938 }
1939 custom_profile=DestroyStringInfo(custom_profile);
1940}
1941
1942static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1943{
1944 DestinationManager
1945 *destination;
1946
1947 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1948 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1949 destination=(DestinationManager *) cinfo->dest;
1950 destination->manager.init_destination=InitializeDestination;
1951 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1952 destination->manager.term_destination=TerminateDestination;
1953 destination->image=image;
1954}
1955
1956static char **SamplingFactorToList(const char *text)
1957{
1958 char
1959 **textlist;
1960
1961 register char
1962 *q;
1963
1964 register const char
1965 *p;
1966
cristybb503372010-05-27 20:51:26 +00001967 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001968 i;
1969
cristybb503372010-05-27 20:51:26 +00001970 size_t
cristy3ed852e2009-09-05 21:47:34 +00001971 lines;
1972
1973 if (text == (char *) NULL)
1974 return((char **) NULL);
1975 /*
1976 Convert string to an ASCII list.
1977 */
1978 lines=1;
1979 for (p=text; *p != '\0'; p++)
1980 if (*p == ',')
1981 lines++;
1982 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1983 sizeof(*textlist));
1984 if (textlist == (char **) NULL)
1985 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1986 p=text;
cristybb503372010-05-27 20:51:26 +00001987 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001988 {
1989 for (q=(char *) p; *q != '\0'; q++)
1990 if (*q == ',')
1991 break;
1992 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1993 sizeof(*textlist[i]));
1994 if (textlist[i] == (char *) NULL)
1995 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1996 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1997 if (*q == '\r')
1998 q++;
1999 p=q+1;
2000 }
2001 textlist[i]=(char *) NULL;
2002 return(textlist);
2003}
2004
2005static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00002006 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002007{
2008 const char
2009 *option,
2010 *sampling_factor,
2011 *value;
2012
2013 ErrorManager
2014 error_manager;
2015
cristy21749e92012-02-18 02:29:21 +00002016 int
2017 quality;
2018
cristy3ed852e2009-09-05 21:47:34 +00002019 JSAMPLE
2020 *jpeg_pixels;
2021
2022 JSAMPROW
2023 scanline[1];
2024
cristy3ed852e2009-09-05 21:47:34 +00002025 MagickBooleanType
2026 status;
2027
2028 register JSAMPLE
2029 *q;
2030
cristybb503372010-05-27 20:51:26 +00002031 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002032 i;
2033
cristy524222d2011-04-25 00:37:06 +00002034 ssize_t
2035 y;
2036
cristy3ed852e2009-09-05 21:47:34 +00002037 struct jpeg_compress_struct
2038 jpeg_info;
2039
2040 struct jpeg_error_mgr
2041 jpeg_error;
2042
2043 /*
2044 Open image file.
2045 */
2046 assert(image_info != (const ImageInfo *) NULL);
2047 assert(image_info->signature == MagickSignature);
2048 assert(image != (Image *) NULL);
2049 assert(image->signature == MagickSignature);
2050 if (image->debug != MagickFalse)
2051 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002052 assert(exception != (ExceptionInfo *) NULL);
2053 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00002054 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002055 if (status == MagickFalse)
2056 return(status);
2057 /*
2058 Initialize JPEG parameters.
2059 */
cristy91044972011-04-22 14:21:16 +00002060 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00002061 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
2062 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
2063 jpeg_info.client_data=(void *) image;
2064 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00002065 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00002066 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00002067 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00002068 error_manager.image=image;
2069 jpeg_pixels=(JSAMPLE *) NULL;
2070 if (setjmp(error_manager.error_recovery) != 0)
2071 {
2072 jpeg_destroy_compress(&jpeg_info);
2073 (void) CloseBlob(image);
2074 return(MagickFalse);
2075 }
2076 jpeg_info.client_data=(void *) &error_manager;
2077 jpeg_create_compress(&jpeg_info);
2078 JPEGDestinationManager(&jpeg_info,image);
2079 if ((image->columns != (unsigned int) image->columns) ||
2080 (image->rows != (unsigned int) image->rows))
2081 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2082 jpeg_info.image_width=(unsigned int) image->columns;
2083 jpeg_info.image_height=(unsigned int) image->rows;
2084 jpeg_info.input_components=3;
2085 jpeg_info.data_precision=8;
2086 jpeg_info.in_color_space=JCS_RGB;
2087 switch (image->colorspace)
2088 {
2089 case CMYKColorspace:
2090 {
2091 jpeg_info.input_components=4;
2092 jpeg_info.in_color_space=JCS_CMYK;
2093 break;
2094 }
2095 case YCbCrColorspace:
2096 case Rec601YCbCrColorspace:
2097 case Rec709YCbCrColorspace:
2098 {
2099 jpeg_info.in_color_space=JCS_YCbCr;
2100 break;
2101 }
2102 case GRAYColorspace:
2103 case Rec601LumaColorspace:
2104 case Rec709LumaColorspace:
2105 {
2106 jpeg_info.input_components=1;
2107 jpeg_info.in_color_space=JCS_GRAYSCALE;
2108 break;
2109 }
2110 default:
cristy9f396782009-12-21 01:37:45 +00002111 {
cristy3d9f5ba2012-06-26 13:37:31 +00002112 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +00002113 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00002114 break;
cristy9f396782009-12-21 01:37:45 +00002115 }
cristy3ed852e2009-09-05 21:47:34 +00002116 }
2117 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00002118 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00002119 {
2120 jpeg_info.input_components=1;
2121 jpeg_info.in_color_space=JCS_GRAYSCALE;
2122 }
2123 jpeg_set_defaults(&jpeg_info);
2124 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
2125 jpeg_info.data_precision=8;
2126 else
2127 if (sizeof(JSAMPLE) > 1)
2128 jpeg_info.data_precision=12;
2129 jpeg_info.density_unit=(UINT8) 1;
2130 if (image->debug != MagickFalse)
2131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2a11bef2011-10-28 18:33:11 +00002132 "Image resolution: %.20g,%.20g",floor(image->resolution.x+0.5),
2133 floor(image->resolution.y+0.5));
2134 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
cristy3ed852e2009-09-05 21:47:34 +00002135 {
2136 /*
2137 Set image resolution.
2138 */
2139 jpeg_info.write_JFIF_header=MagickTrue;
cristy2a11bef2011-10-28 18:33:11 +00002140 jpeg_info.X_density=(UINT16) floor(image->resolution.x+0.5);
2141 jpeg_info.Y_density=(UINT16) floor(image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00002142 if (image->units == PixelsPerInchResolution)
2143 jpeg_info.density_unit=(UINT8) 1;
2144 if (image->units == PixelsPerCentimeterResolution)
2145 jpeg_info.density_unit=(UINT8) 2;
2146 }
cristy97cb3bf2012-02-21 18:31:20 +00002147 jpeg_info.dct_method=JDCT_FLOAT;
cristy3ed852e2009-09-05 21:47:34 +00002148 option=GetImageOption(image_info,"jpeg:dct-method");
2149 if (option != (const char *) NULL)
2150 switch (*option)
2151 {
2152 case 'D':
2153 case 'd':
2154 {
2155 if (LocaleCompare(option,"default") == 0)
2156 jpeg_info.dct_method=JDCT_DEFAULT;
2157 break;
2158 }
2159 case 'F':
2160 case 'f':
2161 {
2162 if (LocaleCompare(option,"fastest") == 0)
2163 jpeg_info.dct_method=JDCT_FASTEST;
2164 if (LocaleCompare(option,"float") == 0)
2165 jpeg_info.dct_method=JDCT_FLOAT;
2166 break;
2167 }
2168 case 'I':
2169 case 'i':
2170 {
2171 if (LocaleCompare(option,"ifast") == 0)
2172 jpeg_info.dct_method=JDCT_IFAST;
2173 if (LocaleCompare(option,"islow") == 0)
2174 jpeg_info.dct_method=JDCT_ISLOW;
2175 break;
2176 }
2177 }
2178 option=GetImageOption(image_info,"jpeg:optimize-coding");
2179 if (option != (const char *) NULL)
anthony6f201312012-03-30 04:08:15 +00002180 jpeg_info.optimize_coding=IsStringTrue(option);
cristy3ed852e2009-09-05 21:47:34 +00002181 else
2182 {
2183 MagickSizeType
2184 length;
2185
2186 length=(MagickSizeType) jpeg_info.input_components*image->columns*
2187 image->rows*sizeof(JSAMPLE);
2188 if (length == (MagickSizeType) ((size_t) length))
2189 {
2190 /*
2191 Perform optimization only if available memory resources permit it.
2192 */
2193 status=AcquireMagickResource(MemoryResource,length);
cristy3ed852e2009-09-05 21:47:34 +00002194 RelinquishMagickResource(MemoryResource,length);
anthony6f201312012-03-30 04:08:15 +00002195 jpeg_info.optimize_coding=status;
cristy3ed852e2009-09-05 21:47:34 +00002196 }
2197 }
2198#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2199 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2200 (image_info->interlace != NoInterlace))
2201 {
2202 if (image->debug != MagickFalse)
2203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2204 "Interlace: progressive");
2205 jpeg_simple_progression(&jpeg_info);
2206 }
2207 else
2208 if (image->debug != MagickFalse)
2209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2210 "Interlace: non-progressive");
2211#else
2212 if (image->debug != MagickFalse)
2213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2214 "Interlace: nonprogressive");
2215#endif
cristy0adb4f92009-11-28 18:08:51 +00002216 option=GetImageOption(image_info,"jpeg:extent");
2217 if (option != (const char *) NULL)
2218 {
2219 Image
2220 *jpeg_image;
2221
2222 ImageInfo
2223 *jpeg_info;
2224
2225 jpeg_info=CloneImageInfo(image_info);
cristy1e178e72011-08-28 19:44:34 +00002226 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00002227 if (jpeg_image != (Image *) NULL)
2228 {
2229 MagickSizeType
2230 extent;
2231
2232 size_t
cristy87e73ab2010-02-12 01:59:12 +00002233 maximum,
2234 minimum;
cristy0adb4f92009-11-28 18:08:51 +00002235
2236 /*
2237 Search for compression quality that does not exceed image extent.
2238 */
2239 jpeg_info->quality=0;
cristyd6495f92011-12-01 19:36:52 +00002240 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00002241 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
2242 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00002243 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00002244 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00002245 {
cristy87e73ab2010-02-12 01:59:12 +00002246 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy1e178e72011-08-28 19:44:34 +00002247 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002248 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00002249 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002250 else
cristy87e73ab2010-02-12 01:59:12 +00002251 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00002252 }
2253 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00002254 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002255 jpeg_image=DestroyImage(jpeg_image);
2256 }
2257 jpeg_info=DestroyImageInfo(jpeg_info);
2258 }
cristy21749e92012-02-18 02:29:21 +00002259 quality=92;
cristy3ed852e2009-09-05 21:47:34 +00002260 if ((image_info->compression != LosslessJPEGCompression) &&
2261 (image->quality <= 100))
2262 {
cristy21749e92012-02-18 02:29:21 +00002263 if (image->quality != UndefinedCompressionQuality)
2264 quality=(int) image->quality;
cristy3ed852e2009-09-05 21:47:34 +00002265 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00002266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2267 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00002268 }
2269 else
2270 {
2271#if !defined(C_LOSSLESS_SUPPORTED)
cristy21749e92012-02-18 02:29:21 +00002272 quality=100;
cristy3ed852e2009-09-05 21:47:34 +00002273 if (image->debug != MagickFalse)
2274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2275#else
2276 if (image->quality < 100)
cristy1e178e72011-08-28 19:44:34 +00002277 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2278 "LosslessToLossyJPEGConversion",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002279 else
2280 {
2281 int
2282 point_transform,
2283 predictor;
2284
2285 predictor=image->quality/100; /* range 1-7 */
2286 point_transform=image->quality % 20; /* range 0-15 */
2287 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2288 if (image->debug != MagickFalse)
2289 {
2290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2291 "Compression: lossless");
2292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2293 "Predictor: %d",predictor);
2294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2295 "Point Transform: %d",point_transform);
2296 }
2297 }
2298#endif
2299 }
cristy21749e92012-02-18 02:29:21 +00002300 jpeg_set_quality(&jpeg_info,quality,MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00002301 sampling_factor=(const char *) NULL;
cristyd15e6592011-10-15 00:13:06 +00002302 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
cristy3ed852e2009-09-05 21:47:34 +00002303 if (value != (char *) NULL)
2304 {
2305 sampling_factor=value;
2306 if (image->debug != MagickFalse)
2307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2308 " Input sampling-factors=%s",sampling_factor);
2309 }
2310 if (image_info->sampling_factor != (char *) NULL)
2311 sampling_factor=image_info->sampling_factor;
2312 if (sampling_factor == (const char *) NULL)
2313 {
2314 if (image->quality >= 90)
2315 for (i=0; i < MAX_COMPONENTS; i++)
2316 {
2317 jpeg_info.comp_info[i].h_samp_factor=1;
2318 jpeg_info.comp_info[i].v_samp_factor=1;
2319 }
2320 }
2321 else
2322 {
2323 char
2324 **factors;
2325
2326 GeometryInfo
2327 geometry_info;
2328
2329 MagickStatusType
2330 flags;
2331
2332 /*
2333 Set sampling factor.
2334 */
2335 i=0;
2336 factors=SamplingFactorToList(sampling_factor);
2337 if (factors != (char **) NULL)
2338 {
2339 for (i=0; i < MAX_COMPONENTS; i++)
2340 {
2341 if (factors[i] == (char *) NULL)
2342 break;
2343 flags=ParseGeometry(factors[i],&geometry_info);
2344 if ((flags & SigmaValue) == 0)
2345 geometry_info.sigma=geometry_info.rho;
2346 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2347 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2348 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2349 }
2350 factors=(char **) RelinquishMagickMemory(factors);
2351 }
2352 for ( ; i < MAX_COMPONENTS; i++)
2353 {
2354 jpeg_info.comp_info[i].h_samp_factor=1;
2355 jpeg_info.comp_info[i].v_samp_factor=1;
2356 }
2357 }
2358 if (jpeg_info.input_components == 1)
2359 for (i=0; i < MAX_COMPONENTS; i++)
2360 {
2361 jpeg_info.comp_info[i].h_samp_factor=1;
2362 jpeg_info.comp_info[i].v_samp_factor=1;
2363 }
cristy1b58f252012-03-01 01:41:41 +00002364 option=GetImageOption(image_info,"jpeg:q-table");
2365 if (option != (const char *) NULL)
cristy1445b322012-02-19 18:57:30 +00002366 {
cristy1b58f252012-03-01 01:41:41 +00002367 QuantizationTable
2368 *table;
cristyb4bb39c2012-02-21 18:45:54 +00002369
cristy1445b322012-02-19 18:57:30 +00002370 /*
cristy1b58f252012-03-01 01:41:41 +00002371 Custom quantization tables.
cristy1445b322012-02-19 18:57:30 +00002372 */
cristy1b58f252012-03-01 01:41:41 +00002373 table=GetQuantizationTable(option,"0",exception);
2374 if (table != (QuantizationTable *) NULL)
2375 {
2376 jpeg_add_quant_table(&jpeg_info,0,table->levels,jpeg_quality_scaling(
2377 quality),0);
2378 table=DestroyQuantizationTable(table);
2379 }
2380 table=GetQuantizationTable(option,"1",exception);
2381 if (table != (QuantizationTable *) NULL)
2382 {
2383 jpeg_add_quant_table(&jpeg_info,1,table->levels,jpeg_quality_scaling(
2384 quality),0);
2385 table=DestroyQuantizationTable(table);
2386 }
2387 table=GetQuantizationTable(option,"2",exception);
2388 if (table != (QuantizationTable *) NULL)
2389 {
2390 jpeg_add_quant_table(&jpeg_info,2,table->levels,jpeg_quality_scaling(
2391 quality),0);
2392 table=DestroyQuantizationTable(table);
2393 }
2394 table=GetQuantizationTable(option,"3",exception);
2395 if (table != (QuantizationTable *) NULL)
2396 {
2397 jpeg_add_quant_table(&jpeg_info,3,table->levels,jpeg_quality_scaling(
2398 quality),0);
2399 table=DestroyQuantizationTable(table);
2400 }
cristy1445b322012-02-19 18:57:30 +00002401 }
cristy3ed852e2009-09-05 21:47:34 +00002402 jpeg_start_compress(&jpeg_info,MagickTrue);
2403 if (image->debug != MagickFalse)
2404 {
2405 if (image->storage_class == PseudoClass)
2406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2407 "Storage class: PseudoClass");
2408 else
2409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2410 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2412 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002413 if (image->colors != 0)
2414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002415 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002416 else
2417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2418 "Number of colors: unspecified");
2419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2420 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2421 switch (image->colorspace)
2422 {
2423 case CMYKColorspace:
2424 {
2425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2426 "Storage class: DirectClass");
2427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2428 "Colorspace: CMYK");
2429 break;
2430 }
2431 case YCbCrColorspace:
2432 case Rec601YCbCrColorspace:
2433 case Rec709YCbCrColorspace:
2434 {
2435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2436 "Colorspace: YCbCr");
2437 break;
2438 }
2439 default:
2440 break;
2441 }
2442 switch (image->colorspace)
2443 {
2444 case CMYKColorspace:
2445 {
2446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2447 "Colorspace: CMYK");
2448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2449 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2450 jpeg_info.comp_info[0].h_samp_factor,
2451 jpeg_info.comp_info[0].v_samp_factor,
2452 jpeg_info.comp_info[1].h_samp_factor,
2453 jpeg_info.comp_info[1].v_samp_factor,
2454 jpeg_info.comp_info[2].h_samp_factor,
2455 jpeg_info.comp_info[2].v_samp_factor,
2456 jpeg_info.comp_info[3].h_samp_factor,
2457 jpeg_info.comp_info[3].v_samp_factor);
2458 break;
2459 }
2460 case GRAYColorspace:
2461 case Rec601LumaColorspace:
2462 case Rec709LumaColorspace:
2463 {
2464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2465 "Colorspace: GRAY");
2466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2467 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2468 jpeg_info.comp_info[0].v_samp_factor);
2469 break;
2470 }
2471 case RGBColorspace:
2472 {
2473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2474 "Image colorspace is RGB");
2475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2476 "Sampling factors: %dx%d,%dx%d,%dx%d",
2477 jpeg_info.comp_info[0].h_samp_factor,
2478 jpeg_info.comp_info[0].v_samp_factor,
2479 jpeg_info.comp_info[1].h_samp_factor,
2480 jpeg_info.comp_info[1].v_samp_factor,
2481 jpeg_info.comp_info[2].h_samp_factor,
2482 jpeg_info.comp_info[2].v_samp_factor);
2483 break;
2484 }
2485 case YCbCrColorspace:
2486 case Rec601YCbCrColorspace:
2487 case Rec709YCbCrColorspace:
2488 {
2489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2490 "Colorspace: YCbCr");
2491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2492 "Sampling factors: %dx%d,%dx%d,%dx%d",
2493 jpeg_info.comp_info[0].h_samp_factor,
2494 jpeg_info.comp_info[0].v_samp_factor,
2495 jpeg_info.comp_info[1].h_samp_factor,
2496 jpeg_info.comp_info[1].v_samp_factor,
2497 jpeg_info.comp_info[2].h_samp_factor,
2498 jpeg_info.comp_info[2].v_samp_factor);
2499 break;
2500 }
2501 default:
2502 {
2503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2504 image->colorspace);
2505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2506 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2507 jpeg_info.comp_info[0].h_samp_factor,
2508 jpeg_info.comp_info[0].v_samp_factor,
2509 jpeg_info.comp_info[1].h_samp_factor,
2510 jpeg_info.comp_info[1].v_samp_factor,
2511 jpeg_info.comp_info[2].h_samp_factor,
2512 jpeg_info.comp_info[2].v_samp_factor,
2513 jpeg_info.comp_info[3].h_samp_factor,
2514 jpeg_info.comp_info[3].v_samp_factor);
2515 break;
2516 }
2517 }
2518 }
2519 /*
2520 Write JPEG profiles.
2521 */
cristyd15e6592011-10-15 00:13:06 +00002522 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002523 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002524 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002525 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2526 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2527 if (image->profiles != (void *) NULL)
2528 WriteProfile(&jpeg_info,image);
2529 /*
2530 Convert MIFF to JPEG raster pixels.
2531 */
2532 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2533 jpeg_info.input_components*sizeof(*jpeg_pixels));
2534 if (jpeg_pixels == (JSAMPLE *) NULL)
2535 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2536 if (setjmp(error_manager.error_recovery) != 0)
2537 {
2538 jpeg_destroy_compress(&jpeg_info);
2539 if (jpeg_pixels != (unsigned char *) NULL)
2540 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2541 (void) CloseBlob(image);
2542 return(MagickFalse);
2543 }
2544 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002545 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002546 {
cristy3ed852e2009-09-05 21:47:34 +00002547 if ((jpeg_info.in_color_space == JCS_RGB) ||
2548 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002549 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002550 {
cristy4c08aed2011-07-01 19:47:50 +00002551 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002552 *p;
2553
cristybb503372010-05-27 20:51:26 +00002554 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002555 x;
2556
cristy1e178e72011-08-28 19:44:34 +00002557 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002558 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002559 break;
2560 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002561 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002562 {
cristy4c08aed2011-07-01 19:47:50 +00002563 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2564 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2565 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002566 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002567 }
2568 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002569 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2570 image->rows);
2571 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002572 break;
2573 }
2574 else
cristye90d7402010-03-14 18:21:29 +00002575 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002576 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002577 {
cristy4c08aed2011-07-01 19:47:50 +00002578 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002579 *p;
2580
cristybb503372010-05-27 20:51:26 +00002581 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002582 x;
2583
cristy1e178e72011-08-28 19:44:34 +00002584 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002585 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002586 break;
2587 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002588 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002589 {
cristy4c08aed2011-07-01 19:47:50 +00002590 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristyed231572011-07-14 02:18:59 +00002591 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002592 }
2593 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002594 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2595 image->rows);
2596 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002597 break;
2598 }
2599 else
cristybb503372010-05-27 20:51:26 +00002600 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002601 {
cristy4c08aed2011-07-01 19:47:50 +00002602 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002603 *p;
2604
cristybb503372010-05-27 20:51:26 +00002605 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002606 x;
2607
cristy1e178e72011-08-28 19:44:34 +00002608 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002609 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002610 break;
2611 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002612 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002613 {
2614 /*
2615 Convert DirectClass packets to contiguous CMYK scanlines.
2616 */
2617 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002618 GetPixelRed(image,p))));
cristye90d7402010-03-14 18:21:29 +00002619 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002620 GetPixelGreen(image,p))));
cristye90d7402010-03-14 18:21:29 +00002621 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002622 GetPixelBlue(image,p))));
cristye90d7402010-03-14 18:21:29 +00002623 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002624 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002625 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002626 }
2627 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002628 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2629 image->rows);
2630 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002631 break;
2632 }
2633 }
2634 else
2635 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002636 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002637 {
cristy4c08aed2011-07-01 19:47:50 +00002638 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002639 *p;
2640
cristybb503372010-05-27 20:51:26 +00002641 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002642 x;
2643
cristy1e178e72011-08-28 19:44:34 +00002644 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002645 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002646 break;
2647 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002648 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002649 {
cristy1fcc8b62012-03-02 17:25:55 +00002650 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002651 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002652 }
2653 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002654 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2655 image->rows);
2656 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002657 break;
2658 }
2659 else
2660 if ((jpeg_info.in_color_space == JCS_RGB) ||
2661 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002662 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002663 {
cristy4c08aed2011-07-01 19:47:50 +00002664 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002665 *p;
2666
cristybb503372010-05-27 20:51:26 +00002667 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002668 x;
2669
cristy1e178e72011-08-28 19:44:34 +00002670 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002671 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002672 break;
2673 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002674 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002675 {
cristy4c08aed2011-07-01 19:47:50 +00002676 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2677 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2678 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002679 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002680 }
2681 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002682 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002683 image->rows);
cristye90d7402010-03-14 18:21:29 +00002684 if (status == MagickFalse)
2685 break;
2686 }
2687 else
cristybb503372010-05-27 20:51:26 +00002688 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002689 {
cristy4c08aed2011-07-01 19:47:50 +00002690 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002691 *p;
2692
cristybb503372010-05-27 20:51:26 +00002693 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002694 x;
2695
cristy1e178e72011-08-28 19:44:34 +00002696 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002697 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002698 break;
2699 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002700 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002701 {
2702 /*
2703 Convert DirectClass packets to contiguous CMYK scanlines.
2704 */
cristye90d7402010-03-14 18:21:29 +00002705 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002706 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002707 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002708 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002709 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002710 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002711 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002712 GetPixelBlack(image,p)) >> 4));
cristyed231572011-07-14 02:18:59 +00002713 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002714 }
2715 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002716 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002717 image->rows);
cristye90d7402010-03-14 18:21:29 +00002718 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002719 break;
2720 }
cristybb503372010-05-27 20:51:26 +00002721 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002722 jpeg_finish_compress(&jpeg_info);
2723 /*
2724 Relinquish resources.
2725 */
2726 jpeg_destroy_compress(&jpeg_info);
2727 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2728 (void) CloseBlob(image);
2729 return(MagickTrue);
2730}
2731#endif