blob: 5756caedc0e9df1611b48a85a81379cad74edd53 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% JJJJJ PPPP EEEEE GGGG %
7% J P P E G %
8% J PPPP EEE G GG %
9% J J P E G G %
10% JJJ P EEEEE GGG %
11% %
12% %
13% Read/Write JPEG Image Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36% This software is based in part on the work of the Independent JPEG Group.
37% See ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz for copyright and
38% licensing restrictions. Blob support contributed by Glenn Randers-Pehrson.
39%
40%
41*/
42
43/*
44 Include declarations.
45*/
46#include "magick/studio.h"
47#include "magick/blob.h"
48#include "magick/blob-private.h"
49#include "magick/cache.h"
50#include "magick/color.h"
cristy316d5172009-09-17 19:31:25 +000051#include "magick/colormap-private.h"
cristy3ed852e2009-09-05 21:47:34 +000052#include "magick/color-private.h"
cristye20ffe52010-04-24 23:51:41 +000053#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000054#include "magick/colorspace.h"
55#include "magick/constitute.h"
56#include "magick/exception.h"
57#include "magick/exception-private.h"
58#include "magick/geometry.h"
59#include "magick/image.h"
60#include "magick/image-private.h"
61#include "magick/list.h"
62#include "magick/log.h"
63#include "magick/magick.h"
64#include "magick/memory_.h"
cristyf2f27272009-12-17 14:48:46 +000065#include "magick/module.h"
cristy3ed852e2009-09-05 21:47:34 +000066#include "magick/monitor.h"
67#include "magick/monitor-private.h"
68#include "magick/option.h"
69#include "magick/profile.h"
70#include "magick/property.h"
71#include "magick/quantum-private.h"
72#include "magick/resource_.h"
73#include "magick/splay-tree.h"
74#include "magick/static.h"
75#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000076#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000077#include "magick/utility.h"
78#include <setjmp.h>
79#if defined(MAGICKCORE_JPEG_DELEGATE)
80#define JPEG_INTERNAL_OPTIONS
81#if defined(__MINGW32__)
82# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
cristye7e40552010-04-24 21:34:22 +000083typedef unsigned char boolean;
cristy3ed852e2009-09-05 21:47:34 +000084#endif
85#undef HAVE_STDLIB_H
86#include "jpeglib.h"
87#include "jerror.h"
88#endif
89
90/*
91 Define declarations.
92*/
93#define ICC_MARKER (JPEG_APP0+2)
94#define ICC_PROFILE "ICC_PROFILE"
95#define IPTC_MARKER (JPEG_APP0+13)
96#define XML_MARKER (JPEG_APP0+1)
97#define MaxBufferExtent 8192
98
99/*
100 Typedef declarations.
101*/
102#if defined(MAGICKCORE_JPEG_DELEGATE)
103typedef struct _DestinationManager
104{
105 struct jpeg_destination_mgr
106 manager;
107
108 Image
109 *image;
110
111 JOCTET
112 *buffer;
113} DestinationManager;
114
115typedef struct _ErrorManager
116{
117 Image
118 *image;
119
120 jmp_buf
121 error_recovery;
122} ErrorManager;
123
124typedef struct _SourceManager
125{
126 struct jpeg_source_mgr
127 manager;
128
129 Image
130 *image;
131
132 JOCTET
133 *buffer;
134
135 boolean
136 start_of_blob;
137} SourceManager;
138#endif
139
140/*
141 Forward declarations.
142*/
143#if defined(MAGICKCORE_JPEG_DELEGATE)
144static MagickBooleanType
145 WriteJPEGImage(const ImageInfo *,Image *);
146#endif
147
148/*
149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150% %
151% %
152% %
153% I s J P E G %
154% %
155% %
156% %
157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158%
159% IsJPEG() returns MagickTrue if the image format type, identified by the
160% magick string, is JPEG.
161%
162% The format of the IsJPEG method is:
163%
164% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
165%
166% A description of each parameter follows:
167%
168% o magick: compare image format pattern against these bytes.
169%
170% o length: Specifies the length of the magick string.
171%
172*/
173static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
174{
175 if (length < 3)
176 return(MagickFalse);
177 if (memcmp(magick,"\377\330\377",3) == 0)
178 return(MagickTrue);
179 return(MagickFalse);
180}
181
182#if defined(MAGICKCORE_JPEG_DELEGATE)
183/*
184%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
185% %
186% %
187% %
188% R e a d J P E G I m a g e %
189% %
190% %
191% %
192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193%
194% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
195% the memory necessary for the new Image structure and returns a pointer to
196% the new image.
197%
198% The format of the ReadJPEGImage method is:
199%
200% Image *ReadJPEGImage(const ImageInfo *image_info,
201% ExceptionInfo *exception)
202%
203% A description of each parameter follows:
204%
205% o image_info: the image info.
206%
207% o exception: return any errors or warnings in this structure.
208%
209*/
210
211static MagickBooleanType EmitMessage(j_common_ptr jpeg_info,int level)
212{
213 char
214 message[JMSG_LENGTH_MAX];
215
216 ErrorManager
217 *error_manager;
218
219 Image
220 *image;
221
222 (jpeg_info->err->format_message)(jpeg_info,message);
223 error_manager=(ErrorManager *) jpeg_info->client_data;
224 image=error_manager->image;
225 if (level < 0)
226 {
227 if ((jpeg_info->err->num_warnings == 0) ||
228 (jpeg_info->err->trace_level >= 3))
229 ThrowBinaryException(CorruptImageWarning,(char *) message,
230 image->filename);
231 jpeg_info->err->num_warnings++;
232 }
233 else
234 if (jpeg_info->err->trace_level >= level)
235 ThrowBinaryException(CoderError,(char *) message,image->filename);
236 return(MagickTrue);
237}
238
239static boolean FillInputBuffer(j_decompress_ptr cinfo)
240{
241 SourceManager
242 *source;
243
244 source=(SourceManager *) cinfo->src;
245 source->manager.bytes_in_buffer=(size_t)
246 ReadBlob(source->image,MaxBufferExtent,source->buffer);
247 if (source->manager.bytes_in_buffer == 0)
248 {
249 if (source->start_of_blob != 0)
250 ERREXIT(cinfo,JERR_INPUT_EMPTY);
251 WARNMS(cinfo,JWRN_JPEG_EOF);
252 source->buffer[0]=(JOCTET) 0xff;
253 source->buffer[1]=(JOCTET) JPEG_EOI;
254 source->manager.bytes_in_buffer=2;
255 }
256 source->manager.next_input_byte=source->buffer;
257 source->start_of_blob=FALSE;
258 return(TRUE);
259}
260
261static int GetCharacter(j_decompress_ptr jpeg_info)
262{
263 if (jpeg_info->src->bytes_in_buffer == 0)
264 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
265 jpeg_info->src->bytes_in_buffer--;
266 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
267}
268
269static void InitializeSource(j_decompress_ptr cinfo)
270{
271 SourceManager
272 *source;
273
274 source=(SourceManager *) cinfo->src;
275 source->start_of_blob=TRUE;
276}
277
cristye8dd1302009-11-11 02:45:03 +0000278static MagickBooleanType IsITUFaxImage(const Image *image)
279{
280 const StringInfo
281 *profile;
282
283 const unsigned char
284 *datum;
285
cristyace6aa42009-11-11 03:17:33 +0000286 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000287 if (profile == (const StringInfo *) NULL)
288 return(MagickFalse);
289 if (GetStringInfoLength(profile) < 5)
290 return(MagickFalse);
291 datum=GetStringInfoDatum(profile);
292 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
293 (datum[3] == 0x41) && (datum[4] == 0x58))
294 return(MagickTrue);
295 return(MagickFalse);
296}
297
cristy3ed852e2009-09-05 21:47:34 +0000298static void JPEGErrorHandler(j_common_ptr jpeg_info)
299{
300 ErrorManager
301 *error_manager;
302
303 (void) EmitMessage(jpeg_info,0);
304 error_manager=(ErrorManager *) jpeg_info->client_data;
305 longjmp(error_manager->error_recovery,1);
306}
307
308static boolean ReadComment(j_decompress_ptr jpeg_info)
309{
310 char
311 *comment;
312
313 ErrorManager
314 *error_manager;
315
316 Image
317 *image;
318
319 register char
320 *p;
321
cristybb503372010-05-27 20:51:26 +0000322 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000323 i;
324
325 size_t
326 length;
327
328 /*
329 Determine length of comment.
330 */
331 error_manager=(ErrorManager *) jpeg_info->client_data;
332 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000333 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000334 length+=GetCharacter(jpeg_info);
335 length-=2;
336 if (length <= 0)
337 return(MagickTrue);
338 comment=(char *) NULL;
339 if (~length >= MaxTextExtent)
340 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
341 sizeof(*comment));
342 if (comment == (char *) NULL)
343 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
344 image->filename);
345 /*
346 Read comment.
347 */
cristybb503372010-05-27 20:51:26 +0000348 i=(ssize_t) length-1;
cristy3ed852e2009-09-05 21:47:34 +0000349 for (p=comment; i-- >= 0; p++)
350 *p=(char) GetCharacter(jpeg_info);
351 *p='\0';
352 (void) SetImageProperty(image,"comment",comment);
353 comment=DestroyString(comment);
354 return(MagickTrue);
355}
356
357static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
358{
359 char
360 magick[12];
361
362 ErrorManager
363 *error_manager;
364
365 Image
366 *image;
367
368 MagickBooleanType
369 status;
370
cristybb503372010-05-27 20:51:26 +0000371 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000372 i;
373
374 register unsigned char
375 *p;
376
377 size_t
378 length;
379
380 StringInfo
381 *icc_profile,
382 *profile;
383
384 /*
385 Read color profile.
386 */
cristybb503372010-05-27 20:51:26 +0000387 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000388 length+=(size_t) GetCharacter(jpeg_info);
389 length-=2;
390 if (length <= 14)
391 {
392 while (length-- > 0)
393 (void) GetCharacter(jpeg_info);
394 return(MagickTrue);
395 }
396 for (i=0; i < 12; i++)
397 magick[i]=(char) GetCharacter(jpeg_info);
398 if (LocaleCompare(magick,ICC_PROFILE) != 0)
399 {
400 /*
401 Not a ICC profile, return.
402 */
cristybb503372010-05-27 20:51:26 +0000403 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000404 (void) GetCharacter(jpeg_info);
405 return(MagickTrue);
406 }
407 (void) GetCharacter(jpeg_info); /* id */
408 (void) GetCharacter(jpeg_info); /* markers */
409 length-=14;
410 error_manager=(ErrorManager *) jpeg_info->client_data;
411 image=error_manager->image;
412 profile=AcquireStringInfo(length);
413 if (profile == (StringInfo *) NULL)
414 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
415 image->filename);
416 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000417 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000418 *p++=(unsigned char) GetCharacter(jpeg_info);
419 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
420 if (icc_profile != (StringInfo *) NULL)
421 {
422 ConcatenateStringInfo(icc_profile,profile);
423 profile=DestroyStringInfo(profile);
424 }
425 else
426 {
427 status=SetImageProfile(image,"icc",profile);
428 profile=DestroyStringInfo(profile);
429 if (status == MagickFalse)
430 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
431 image->filename);
432 }
433 if (image->debug != MagickFalse)
434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000435 "Profile: ICC, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000436 return(MagickTrue);
437}
438
439static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
440{
441 char
442 magick[MaxTextExtent];
443
444 ErrorManager
445 *error_manager;
446
447 Image
448 *image;
449
450 MagickBooleanType
451 status;
452
cristybb503372010-05-27 20:51:26 +0000453 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000454 i;
455
456 register unsigned char
457 *p;
458
459 size_t
460 length;
461
462 StringInfo
463 *iptc_profile,
464 *profile;
465
466 /*
467 Determine length of binary data stored here.
468 */
cristybb503372010-05-27 20:51:26 +0000469 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000470 length+=(size_t) GetCharacter(jpeg_info);
471 length-=2;
472 if (length <= 14)
473 {
474 while (length-- > 0)
475 (void) GetCharacter(jpeg_info);
476 return(MagickTrue);
477 }
478 /*
479 Validate that this was written as a Photoshop resource format slug.
480 */
481 for (i=0; i < 10; i++)
482 magick[i]=(char) GetCharacter(jpeg_info);
483 magick[10]='\0';
484 if (length <= 10)
485 return(MagickTrue);
486 length-=10;
487 if (LocaleCompare(magick,"Photoshop ") != 0)
488 {
489 /*
490 Not a IPTC profile, return.
491 */
cristybb503372010-05-27 20:51:26 +0000492 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000493 (void) GetCharacter(jpeg_info);
494 return(MagickTrue);
495 }
496 /*
497 Remove the version number.
498 */
499 for (i=0; i < 4; i++)
500 (void) GetCharacter(jpeg_info);
501 if (length <= 4)
502 return(MagickTrue);
503 length-=4;
504 if (length == 0)
505 return(MagickTrue);
506 error_manager=(ErrorManager *) jpeg_info->client_data;
507 image=error_manager->image;
508 profile=AcquireStringInfo(length);
509 if (profile == (StringInfo *) NULL)
510 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
511 image->filename);
512 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000513 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000514 *p++=(unsigned char) GetCharacter(jpeg_info);
515 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
516 if (iptc_profile != (StringInfo *) NULL)
517 {
518 ConcatenateStringInfo(iptc_profile,profile);
519 profile=DestroyStringInfo(profile);
520 }
521 else
522 {
cristy595f1432010-12-11 16:41:53 +0000523 status=SetImageProfile(image,"8bim",profile);
cristy3ed852e2009-09-05 21:47:34 +0000524 profile=DestroyStringInfo(profile);
525 if (status == MagickFalse)
526 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
527 image->filename);
528 }
529 if (image->debug != MagickFalse)
530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000531 "Profile: iptc, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000532 return(MagickTrue);
533}
534
535static boolean ReadProfile(j_decompress_ptr jpeg_info)
536{
537 char
538 name[MaxTextExtent];
539
540 ErrorManager
541 *error_manager;
542
543 Image
544 *image;
545
546 int
547 marker;
548
549 MagickBooleanType
550 status;
551
cristybb503372010-05-27 20:51:26 +0000552 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000553 i;
554
555 register unsigned char
556 *p;
557
558 size_t
559 length;
560
561 StringInfo
562 *profile;
563
564 /*
565 Read generic profile.
566 */
cristybb503372010-05-27 20:51:26 +0000567 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000568 length+=(size_t) GetCharacter(jpeg_info);
569 if (length <= 2)
570 return(MagickTrue);
571 length-=2;
572 marker=jpeg_info->unread_marker-JPEG_APP0;
573 (void) FormatMagickString(name,MaxTextExtent,"APP%d",marker);
574 error_manager=(ErrorManager *) jpeg_info->client_data;
575 image=error_manager->image;
576 profile=AcquireStringInfo(length);
577 if (profile == (StringInfo *) NULL)
578 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
579 image->filename);
580 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000581 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000582 *p++=(unsigned char) GetCharacter(jpeg_info);
583 if (marker == 1)
584 {
585 p=GetStringInfoDatum(profile);
586 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
587 (void) CopyMagickString(name,"exif",MaxTextExtent);
588 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
589 {
cristybb503372010-05-27 20:51:26 +0000590 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000591 j;
592
593 /*
594 Extract namespace from XMP profile.
595 */
596 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000597 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000598 {
599 if (*p == '\0')
600 break;
601 p++;
602 }
cristybb503372010-05-27 20:51:26 +0000603 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000604 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
605 (void) CopyMagickString(name,"xmp",MaxTextExtent);
606 }
607 }
608 status=SetImageProfile(image,name,profile);
609 profile=DestroyStringInfo(profile);
610 if (status == MagickFalse)
611 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
612 image->filename);
613 if (image->debug != MagickFalse)
614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000615 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000616 return(MagickTrue);
617}
618
cristyf2faecf2010-05-28 19:19:36 +0000619static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000620{
621 SourceManager
622 *source;
623
624 if (number_bytes <= 0)
625 return;
626 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000627 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000628 {
cristyf2faecf2010-05-28 19:19:36 +0000629 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000630 (void) FillInputBuffer(cinfo);
631 }
cristy4cb162a2010-05-30 03:04:47 +0000632 source->manager.next_input_byte+=number_bytes;
633 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000634}
635
636static void TerminateSource(j_decompress_ptr cinfo)
637{
638 cinfo=cinfo;
639}
640
641static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
642{
643 SourceManager
644 *source;
645
646 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
647 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
648 source=(SourceManager *) cinfo->src;
649 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
650 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
651 source=(SourceManager *) cinfo->src;
652 source->manager.init_source=InitializeSource;
653 source->manager.fill_input_buffer=FillInputBuffer;
654 source->manager.skip_input_data=SkipInputData;
655 source->manager.resync_to_restart=jpeg_resync_to_restart;
656 source->manager.term_source=TerminateSource;
657 source->manager.bytes_in_buffer=0;
658 source->manager.next_input_byte=NULL;
659 source->image=image;
660}
661
662static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
663 Image *image)
664{
665 image->quality=UndefinedCompressionQuality;
666#if defined(D_PROGRESSIVE_SUPPORTED)
667 if (image->compression == LosslessJPEGCompression)
668 {
669 image->quality=100;
670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
671 "Quality: 100 (lossless)");
672 }
673 else
674#endif
675 {
cristybb503372010-05-27 20:51:26 +0000676 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000677 j,
678 qvalue,
679 sum;
680
cristybb503372010-05-27 20:51:26 +0000681 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000682 i;
683
684 /*
685 Determine the JPEG compression quality from the quantization tables.
686 */
687 sum=0;
688 for (i=0; i < NUM_QUANT_TBLS; i++)
689 {
690 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
691 for (j=0; j < DCTSIZE2; j++)
692 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
693 }
694 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
695 (jpeg_info->quant_tbl_ptrs[1] != NULL))
696 {
cristybb503372010-05-27 20:51:26 +0000697 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000698 hash[101] =
699 {
700 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
701 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
702 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
703 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
704 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
705 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
706 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
707 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
708 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
709 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
710 0
711 },
712 sums[101] =
713 {
714 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
715 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
716 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
717 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
718 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
719 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
720 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
721 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
722 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
723 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
724 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
725 128, 0
726 };
727
cristybb503372010-05-27 20:51:26 +0000728 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000729 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
730 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
731 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
732 for (i=0; i < 100; i++)
733 {
734 if ((qvalue < hash[i]) && (sum < sums[i]))
735 continue;
cristyb6c017e2010-01-13 19:11:39 +0000736 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000737 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000738 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000740 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000741 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000742 break;
743 }
744 }
745 else
746 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
747 {
cristybb503372010-05-27 20:51:26 +0000748 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000749 hash[101] =
750 {
751 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
752 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
753 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
754 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
755 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
756 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
757 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
758 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
759 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
760 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
761 0
762 },
763 sums[101] =
764 {
765 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
766 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
767 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
768 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
769 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
770 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
771 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
772 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
773 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
774 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
775 667, 592, 518, 441, 369, 292, 221, 151, 86,
776 64, 0
777 };
778
cristybb503372010-05-27 20:51:26 +0000779 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000780 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
781 for (i=0; i < 100; i++)
782 {
783 if ((qvalue < hash[i]) && (sum < sums[i]))
784 continue;
cristyb6c017e2010-01-13 19:11:39 +0000785 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000786 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000787 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000789 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000790 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000791 break;
792 }
793 }
794 }
795}
796
797static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image)
798{
799 char
800 sampling_factor[MaxTextExtent];
801
802 switch (jpeg_info->out_color_space)
803 {
804 case JCS_CMYK:
805 {
806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
807 (void) FormatMagickString(sampling_factor,MaxTextExtent,
808 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
809 jpeg_info->comp_info[0].v_samp_factor,
810 jpeg_info->comp_info[1].h_samp_factor,
811 jpeg_info->comp_info[1].v_samp_factor,
812 jpeg_info->comp_info[2].h_samp_factor,
813 jpeg_info->comp_info[2].v_samp_factor,
814 jpeg_info->comp_info[3].h_samp_factor,
815 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000816 break;
cristy3ed852e2009-09-05 21:47:34 +0000817 }
818 case JCS_GRAYSCALE:
819 {
820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
821 "Colorspace: GRAYSCALE");
822 (void) FormatMagickString(sampling_factor,MaxTextExtent,"%dx%d",
823 jpeg_info->comp_info[0].h_samp_factor,
824 jpeg_info->comp_info[0].v_samp_factor);
825 break;
826 }
827 case JCS_RGB:
828 {
829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
830 (void) FormatMagickString(sampling_factor,MaxTextExtent,
831 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
832 jpeg_info->comp_info[0].v_samp_factor,
833 jpeg_info->comp_info[1].h_samp_factor,
834 jpeg_info->comp_info[1].v_samp_factor,
835 jpeg_info->comp_info[2].h_samp_factor,
836 jpeg_info->comp_info[2].v_samp_factor);
837 break;
838 }
839 default:
840 {
841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
842 jpeg_info->out_color_space);
843 (void) FormatMagickString(sampling_factor,MaxTextExtent,
844 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
845 jpeg_info->comp_info[0].v_samp_factor,
846 jpeg_info->comp_info[1].h_samp_factor,
847 jpeg_info->comp_info[1].v_samp_factor,
848 jpeg_info->comp_info[2].h_samp_factor,
849 jpeg_info->comp_info[2].v_samp_factor,
850 jpeg_info->comp_info[3].h_samp_factor,
851 jpeg_info->comp_info[3].v_samp_factor);
852 break;
853 }
854 }
855 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor);
cristye90d7402010-03-14 18:21:29 +0000856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
857 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000858}
859
860static Image *ReadJPEGImage(const ImageInfo *image_info,
861 ExceptionInfo *exception)
862{
863 char
864 value[MaxTextExtent];
865
cristycaec74e2009-09-14 02:20:04 +0000866 const char
cristy11151212009-09-14 13:10:15 +0000867 *option;
cristycaec74e2009-09-14 02:20:04 +0000868
cristy3ed852e2009-09-05 21:47:34 +0000869 ErrorManager
870 error_manager;
871
cristy3ed852e2009-09-05 21:47:34 +0000872 Image
873 *image;
874
cristya0511732010-02-17 02:38:35 +0000875 IndexPacket
876 index;
877
cristybb503372010-05-27 20:51:26 +0000878 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000879 y;
880
881 JSAMPLE
882 *jpeg_pixels;
883
884 JSAMPROW
885 scanline[1];
886
887 MagickBooleanType
888 debug,
889 status;
890
891 MagickSizeType
892 number_pixels;
893
cristybb503372010-05-27 20:51:26 +0000894 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000895 i;
896
897 struct jpeg_decompress_struct
898 jpeg_info;
899
900 struct jpeg_error_mgr
901 jpeg_error;
902
903 register JSAMPLE
904 *p;
905
cristybb503372010-05-27 20:51:26 +0000906 size_t
cristy3ed852e2009-09-05 21:47:34 +0000907 precision,
908 units;
909
910 /*
911 Open image file.
912 */
913 assert(image_info != (const ImageInfo *) NULL);
914 assert(image_info->signature == MagickSignature);
915 if (image_info->debug != MagickFalse)
916 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
917 image_info->filename);
918 assert(exception != (ExceptionInfo *) NULL);
919 assert(exception->signature == MagickSignature);
920 debug=IsEventLogging();
921 image=AcquireImage(image_info);
922 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
923 if (status == MagickFalse)
924 {
925 image=DestroyImageList(image);
926 return((Image *) NULL);
927 }
928 /*
929 Initialize JPEG parameters.
930 */
931 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
932 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
933 jpeg_info.err=jpeg_std_error(&jpeg_error);
934 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
935 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
936 jpeg_pixels=(JSAMPLE *) NULL;
937 error_manager.image=image;
938 if (setjmp(error_manager.error_recovery) != 0)
939 {
940 jpeg_destroy_decompress(&jpeg_info);
941 (void) CloseBlob(image);
942 number_pixels=(MagickSizeType) image->columns*image->rows;
943 if (number_pixels != 0)
944 return(GetFirstImageInList(image));
cristy195f6d12009-11-05 18:16:03 +0000945 InheritException(exception,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +0000946 return(DestroyImage(image));
947 }
948 jpeg_info.client_data=(void *) &error_manager;
949 jpeg_create_decompress(&jpeg_info);
950 JPEGSourceManager(&jpeg_info,image);
951 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
952 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
953 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
954 for (i=1; i < 16; i++)
955 if ((i != 2) && (i != 13) && (i != 14))
956 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +0000957 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +0000958 if ((image_info->colorspace == YCbCrColorspace) ||
959 (image_info->colorspace == Rec601YCbCrColorspace) ||
960 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +0000961 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +0000962 if (IsITUFaxImage(image) != MagickFalse)
963 {
964 image->colorspace=LabColorspace;
965 jpeg_info.out_color_space=JCS_YCbCr;
966 }
967 else
968 if (jpeg_info.out_color_space == JCS_CMYK)
969 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000970 /*
971 Set image resolution.
972 */
973 units=0;
974 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
975 (jpeg_info.Y_density != 1))
976 {
977 image->x_resolution=(double) jpeg_info.X_density;
978 image->y_resolution=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +0000979 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +0000980 }
981 if (units == 1)
982 image->units=PixelsPerInchResolution;
983 if (units == 2)
984 image->units=PixelsPerCentimeterResolution;
985 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +0000986 option=GetImageOption(image_info,"jpeg:size");
987 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000988 {
989 double
990 scale_factor;
991
cristycaec74e2009-09-14 02:20:04 +0000992 GeometryInfo
993 geometry_info;
994
cristy0adb4f92009-11-28 18:08:51 +0000995 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +0000996 flags;
997
cristy3ed852e2009-09-05 21:47:34 +0000998 /*
cristycaec74e2009-09-14 02:20:04 +0000999 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001000 */
cristy11151212009-09-14 13:10:15 +00001001 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001002 if ((flags & SigmaValue) == 0)
1003 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001004 jpeg_calc_output_dimensions(&jpeg_info);
1005 image->magick_columns=jpeg_info.output_width;
1006 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001007 scale_factor=1.0;
1008 if (geometry_info.rho != 0.0)
1009 scale_factor=jpeg_info.output_width/geometry_info.rho;
1010 if ((geometry_info.sigma != 0.0) &&
1011 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1012 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001013 jpeg_info.scale_num=1U;
1014 jpeg_info.scale_denom=(unsigned int) scale_factor;
1015 jpeg_calc_output_dimensions(&jpeg_info);
1016 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1018 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001019 }
cristybb503372010-05-27 20:51:26 +00001020 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001021#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1022#if defined(D_LOSSLESS_SUPPORTED)
1023 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1024 JPEGInterlace : NoInterlace;
1025 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1026 LosslessJPEGCompression : JPEGCompression;
1027 if (jpeg_info.data_precision > 8)
1028 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1029 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1030 image->filename);
1031 if (jpeg_info.data_precision == 16)
1032 jpeg_info.data_precision=12;
1033#else
1034 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1035 NoInterlace;
1036 image->compression=JPEGCompression;
1037#endif
1038#else
1039 image->compression=JPEGCompression;
1040 image->interlace=JPEGInterlace;
1041#endif
1042 if ((image_info->colors > 8) && (image_info->colors <= 256))
1043 {
1044 /*
1045 Let the JPEG library quantize for us.
1046 */
1047 jpeg_info.quantize_colors=MagickTrue;
1048 jpeg_info.desired_number_of_colors=(int) image_info->colors;
1049 }
cristy1b8d4462009-10-27 13:46:56 +00001050 option=GetImageOption(image_info,"jpeg:block-smoothing");
1051 if (option != (const char *) NULL)
1052 {
1053 jpeg_info.do_block_smoothing=MagickFalse;
1054 if (IsMagickTrue(option) != MagickFalse)
1055 jpeg_info.do_block_smoothing=MagickTrue;
1056 }
cristy787d4352010-03-06 13:55:58 +00001057 option=GetImageOption(image_info,"jpeg:dct-method");
1058 if (option != (const char *) NULL)
1059 switch (*option)
1060 {
1061 case 'D':
1062 case 'd':
1063 {
1064 if (LocaleCompare(option,"default") == 0)
1065 jpeg_info.dct_method=JDCT_DEFAULT;
1066 break;
1067 }
1068 case 'F':
1069 case 'f':
1070 {
1071 if (LocaleCompare(option,"fastest") == 0)
1072 jpeg_info.dct_method=JDCT_FASTEST;
1073 if (LocaleCompare(option,"float") == 0)
1074 jpeg_info.dct_method=JDCT_FLOAT;
1075 break;
1076 }
1077 case 'I':
1078 case 'i':
1079 {
1080 if (LocaleCompare(option,"ifast") == 0)
1081 jpeg_info.dct_method=JDCT_IFAST;
1082 if (LocaleCompare(option,"islow") == 0)
1083 jpeg_info.dct_method=JDCT_ISLOW;
1084 break;
1085 }
1086 }
cristy1b8d4462009-10-27 13:46:56 +00001087 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1088 if (option != (const char *) NULL)
1089 {
1090 jpeg_info.do_fancy_upsampling=MagickFalse;
1091 if (IsMagickTrue(option) != MagickFalse)
1092 jpeg_info.do_fancy_upsampling=MagickTrue;
1093 }
cristy3ed852e2009-09-05 21:47:34 +00001094 (void) jpeg_start_decompress(&jpeg_info);
1095 image->columns=jpeg_info.output_width;
1096 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001097 image->depth=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001098 if (jpeg_info.out_color_space == JCS_YCbCr)
1099 image->colorspace=YCbCrColorspace;
1100 if (jpeg_info.out_color_space == JCS_CMYK)
1101 image->colorspace=CMYKColorspace;
1102 if ((image_info->colors != 0) && (image_info->colors <= 256))
1103 if (AcquireImageColormap(image,image_info->colors) == MagickFalse)
1104 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1105 if ((jpeg_info.output_components == 1) &&
1106 (jpeg_info.quantize_colors == MagickFalse))
1107 {
cristybb503372010-05-27 20:51:26 +00001108 size_t
cristy3ed852e2009-09-05 21:47:34 +00001109 colors;
1110
cristybb503372010-05-27 20:51:26 +00001111 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy3ed852e2009-09-05 21:47:34 +00001112 if (AcquireImageColormap(image,colors) == MagickFalse)
1113 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1114 }
1115 if (image->debug != MagickFalse)
1116 {
1117 if (image->interlace != NoInterlace)
1118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1119 "Interlace: progressive");
1120 else
1121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1122 "Interlace: nonprogressive");
1123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1124 (int) jpeg_info.data_precision);
1125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1126 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1127 }
1128 JPEGSetImageQuality(&jpeg_info,image);
1129 JPEGSetImageSamplingFactor(&jpeg_info,image);
cristye8c25f92010-06-03 00:53:06 +00001130 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001131 jpeg_info.out_color_space);
1132 (void) SetImageProperty(image,"jpeg:colorspace",value);
1133 if (image_info->ping != MagickFalse)
1134 {
1135 jpeg_destroy_decompress(&jpeg_info);
1136 (void) CloseBlob(image);
1137 return(GetFirstImageInList(image));
1138 }
1139 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1140 jpeg_info.output_components*sizeof(JSAMPLE));
1141 if (jpeg_pixels == (JSAMPLE *) NULL)
1142 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1143 /*
1144 Convert JPEG pixels to pixel packets.
1145 */
1146 if (setjmp(error_manager.error_recovery) != 0)
1147 {
1148 if (jpeg_pixels != (unsigned char *) NULL)
1149 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1150 jpeg_destroy_decompress(&jpeg_info);
1151 (void) CloseBlob(image);
1152 number_pixels=(MagickSizeType) image->columns*image->rows;
1153 if (number_pixels != 0)
1154 return(GetFirstImageInList(image));
1155 return(DestroyImage(image));
1156 }
1157 if (jpeg_info.quantize_colors != MagickFalse)
1158 {
cristybb503372010-05-27 20:51:26 +00001159 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001160 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001161 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001162 {
1163 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1164 image->colormap[i].green=image->colormap[i].red;
1165 image->colormap[i].blue=image->colormap[i].red;
1166 image->colormap[i].opacity=OpaqueOpacity;
1167 }
1168 else
cristybb503372010-05-27 20:51:26 +00001169 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001170 {
1171 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1172 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1173 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
1174 image->colormap[i].opacity=OpaqueOpacity;
1175 }
1176 }
1177 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001178 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001179 {
1180 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001181 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001182
cristybb503372010-05-27 20:51:26 +00001183 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001184 x;
1185
1186 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001187 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001188
1189 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1190 {
1191 (void) ThrowMagickException(exception,GetMagickModule(),
1192 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1193 continue;
1194 }
1195 p=jpeg_pixels;
1196 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1197 if (q == (PixelPacket *) NULL)
1198 break;
1199 indexes=GetAuthenticIndexQueue(image);
1200 if (jpeg_info.data_precision > 8)
1201 {
1202 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001203 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001204 {
cristybb503372010-05-27 20:51:26 +00001205 size_t
cristy3ed852e2009-09-05 21:47:34 +00001206 pixel;
1207
1208 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001209 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001210 else
cristybb503372010-05-27 20:51:26 +00001211 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristya0511732010-02-17 02:38:35 +00001212 index=ConstrainColormapIndex(image,pixel);
1213 indexes[x]=index;
1214 *q++=image->colormap[(int) index];
cristy3ed852e2009-09-05 21:47:34 +00001215 p++;
1216 }
1217 else
1218 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001219 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001220 {
1221 q->red=ScaleShortToQuantum((unsigned char)
1222 (GETJSAMPLE(*p++) << 4));
1223 q->green=ScaleShortToQuantum((unsigned char)
1224 (GETJSAMPLE(*p++) << 4));
1225 q->blue=ScaleShortToQuantum((unsigned char)
1226 (GETJSAMPLE(*p++) << 4));
cristyce70c172010-01-07 17:15:30 +00001227 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001228 q++;
1229 }
1230 else
cristybb503372010-05-27 20:51:26 +00001231 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001232 {
1233 q->red=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1234 (GETJSAMPLE(*p++) << 4));
1235 q->green=(Quantum) QuantumRange-ScaleShortToQuantum(
1236 (unsigned char) (GETJSAMPLE(*p++) << 4));
1237 q->blue=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1238 (GETJSAMPLE(*p++) << 4));
cristyce70c172010-01-07 17:15:30 +00001239 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001240 indexes[x]=(IndexPacket) QuantumRange-ScaleShortToQuantum(
1241 (unsigned char) (GETJSAMPLE(*p++) << 4));
1242 q++;
1243 }
1244 }
1245 else
1246 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001247 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001248 {
cristybb503372010-05-27 20:51:26 +00001249 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p));
cristya0511732010-02-17 02:38:35 +00001250 indexes[x]=(IndexPacket) index;
1251 *q++=image->colormap[(int) index];
cristy3ed852e2009-09-05 21:47:34 +00001252 p++;
1253 }
1254 else
1255 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001256 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001257 {
1258 q->red=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1259 q->green=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1260 q->blue=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
cristyce70c172010-01-07 17:15:30 +00001261 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001262 q++;
1263 }
1264 else
cristybb503372010-05-27 20:51:26 +00001265 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001266 {
1267 q->red=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1268 GETJSAMPLE(*p++));
1269 q->green=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1270 GETJSAMPLE(*p++));
1271 q->blue=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1272 GETJSAMPLE(*p++));
cristyce70c172010-01-07 17:15:30 +00001273 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001274 indexes[x]=(IndexPacket) QuantumRange-ScaleCharToQuantum(
1275 (unsigned char) GETJSAMPLE(*p++));
1276 q++;
1277 }
1278 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1279 break;
cristy4cb162a2010-05-30 03:04:47 +00001280 if (SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001281 break;
1282 }
1283 /*
1284 Free jpeg resources.
1285 */
1286 (void) jpeg_finish_decompress(&jpeg_info);
1287 jpeg_destroy_decompress(&jpeg_info);
1288 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1289 (void) CloseBlob(image);
1290 return(GetFirstImageInList(image));
1291}
1292#endif
1293
1294/*
1295%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1296% %
1297% %
1298% %
1299% R e g i s t e r J P E G I m a g e %
1300% %
1301% %
1302% %
1303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1304%
1305% RegisterJPEGImage() adds properties for the JPEG image format to
1306% the list of supported formats. The properties include the image format
1307% tag, a method to read and/or write the format, whether the format
1308% supports the saving of more than one frame to the same file or blob,
1309% whether the format supports native in-memory I/O, and a brief
1310% description of the format.
1311%
1312% The format of the RegisterJPEGImage method is:
1313%
cristybb503372010-05-27 20:51:26 +00001314% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001315%
1316*/
cristybb503372010-05-27 20:51:26 +00001317ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001318{
1319 char
1320 version[MaxTextExtent];
1321
1322 MagickInfo
1323 *entry;
1324
1325 static const char
cristy7138c592009-09-08 13:58:52 +00001326 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001327
1328 *version='\0';
1329#if defined(JPEG_LIB_VERSION)
1330 (void) FormatMagickString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
1331#endif
1332 entry=SetMagickInfo("JPEG");
1333 entry->thread_support=NoThreadSupport;
1334#if defined(MAGICKCORE_JPEG_DELEGATE)
1335 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1336 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1337#endif
1338 entry->magick=(IsImageFormatHandler *) IsJPEG;
1339 entry->adjoin=MagickFalse;
1340 entry->description=ConstantString(description);
1341 if (*version != '\0')
1342 entry->version=ConstantString(version);
1343 entry->module=ConstantString("JPEG");
1344 (void) RegisterMagickInfo(entry);
1345 entry=SetMagickInfo("JPG");
1346 entry->thread_support=NoThreadSupport;
1347#if defined(MAGICKCORE_JPEG_DELEGATE)
1348 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1349 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1350#endif
1351 entry->adjoin=MagickFalse;
1352 entry->description=ConstantString(description);
1353 if (*version != '\0')
1354 entry->version=ConstantString(version);
1355 entry->module=ConstantString("JPEG");
1356 (void) RegisterMagickInfo(entry);
1357 entry=SetMagickInfo("PJPEG");
1358 entry->thread_support=NoThreadSupport;
1359#if defined(MAGICKCORE_JPEG_DELEGATE)
1360 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1361 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1362#endif
1363 entry->adjoin=MagickFalse;
1364 entry->description=ConstantString(description);
1365 if (*version != '\0')
1366 entry->version=ConstantString(version);
1367 entry->module=ConstantString("JPEG");
1368 (void) RegisterMagickInfo(entry);
1369 return(MagickImageCoderSignature);
1370}
1371
1372/*
1373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1374% %
1375% %
1376% %
1377% U n r e g i s t e r J P E G I m a g e %
1378% %
1379% %
1380% %
1381%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1382%
1383% UnregisterJPEGImage() removes format registrations made by the
1384% JPEG module from the list of supported formats.
1385%
1386% The format of the UnregisterJPEGImage method is:
1387%
1388% UnregisterJPEGImage(void)
1389%
1390*/
1391ModuleExport void UnregisterJPEGImage(void)
1392{
1393 (void) UnregisterMagickInfo("PJPG");
1394 (void) UnregisterMagickInfo("JPEG");
1395 (void) UnregisterMagickInfo("JPG");
1396}
1397
1398#if defined(MAGICKCORE_JPEG_DELEGATE)
1399/*
1400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401% %
1402% %
1403% %
1404% W r i t e J P E G I m a g e %
1405% %
1406% %
1407% %
1408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1409%
1410% WriteJPEGImage() writes a JPEG image file and returns it. It
1411% allocates the memory necessary for the new Image structure and returns a
1412% pointer to the new image.
1413%
1414% The format of the WriteJPEGImage method is:
1415%
1416% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,Image *image)
1417%
1418% A description of each parameter follows:
1419%
1420% o image_info: the image info.
1421%
1422% o jpeg_image: The image.
1423%
1424%
1425*/
1426
1427static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1428{
1429 DestinationManager
1430 *destination;
1431
1432 destination=(DestinationManager *) cinfo->dest;
1433 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1434 MaxBufferExtent,destination->buffer);
1435 if (destination->manager.free_in_buffer != MaxBufferExtent)
1436 ERREXIT(cinfo,JERR_FILE_WRITE);
1437 destination->manager.next_output_byte=destination->buffer;
1438 return(TRUE);
1439}
1440
1441static void InitializeDestination(j_compress_ptr cinfo)
1442{
1443 DestinationManager
1444 *destination;
1445
1446 destination=(DestinationManager *) cinfo->dest;
1447 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1448 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1449 destination->manager.next_output_byte=destination->buffer;
1450 destination->manager.free_in_buffer=MaxBufferExtent;
1451}
1452
1453static inline size_t MagickMin(const size_t x,const size_t y)
1454{
1455 if (x < y)
1456 return(x);
1457 return(y);
1458}
1459
1460static void TerminateDestination(j_compress_ptr cinfo)
1461{
1462 DestinationManager
1463 *destination;
1464
1465 destination=(DestinationManager *) cinfo->dest;
1466 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1467 {
1468 ssize_t
1469 count;
1470
1471 count=WriteBlob(destination->image,MaxBufferExtent-
1472 destination->manager.free_in_buffer,destination->buffer);
1473 if (count != (ssize_t)
1474 (MaxBufferExtent-destination->manager.free_in_buffer))
1475 ERREXIT(cinfo,JERR_FILE_WRITE);
1476 }
1477}
1478
1479static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1480{
1481 const char
1482 *name;
1483
1484 const StringInfo
1485 *profile;
1486
1487 MagickBooleanType
1488 iptc;
1489
cristybb503372010-05-27 20:51:26 +00001490 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001491 i;
1492
1493 size_t
1494 length;
1495
1496 StringInfo
1497 *custom_profile;
1498
cristybb503372010-05-27 20:51:26 +00001499 size_t
cristy3ed852e2009-09-05 21:47:34 +00001500 tag_length;
1501
1502 /*
1503 Save image profile as a APP marker.
1504 */
1505 iptc=MagickFalse;
1506 custom_profile=AcquireStringInfo(65535L);
1507 ResetImageProfileIterator(image);
1508 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1509 {
cristy109e5572010-09-16 18:38:17 +00001510 register unsigned char
1511 *p;
1512
cristy3ed852e2009-09-05 21:47:34 +00001513 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001514 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001515 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001516 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001517 {
1518 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1519 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1520 (unsigned int) length);
1521 }
1522 if (LocaleCompare(name,"ICC") == 0)
1523 {
1524 register unsigned char
1525 *p;
1526
1527 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001528 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001529 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001530 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001531 {
1532 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001533 p[12]=(unsigned char) ((i/65519L)+1);
1534 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1535 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1536 length);
1537 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1538 custom_profile),(unsigned int) (length+tag_length));
1539 }
1540 }
1541 if (((LocaleCompare(name,"IPTC") == 0) ||
1542 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1543 {
cristybb503372010-05-27 20:51:26 +00001544 size_t
cristy3ed852e2009-09-05 21:47:34 +00001545 roundup;
1546
1547 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001548 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001549 {
1550 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001551 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001552 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1553 {
1554 (void) memcpy(p,"Photoshop 3.0 ",14);
1555 tag_length=14;
1556 }
1557 else
1558 {
1559 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1560 tag_length=26;
1561 p[24]=(unsigned char) (length >> 8);
1562 p[25]=(unsigned char) (length & 0xff);
1563 }
1564 p[13]=0x00;
1565 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001566 if (roundup != 0)
1567 p[length+tag_length]='\0';
1568 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1569 custom_profile),(unsigned int) (length+tag_length+roundup));
1570 }
1571 }
1572 if (LocaleCompare(name,"XMP") == 0)
1573 {
1574 StringInfo
1575 *xmp_profile;
1576
1577 /*
1578 Add namespace to XMP profile.
1579 */
1580 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/");
1581 ConcatenateStringInfo(xmp_profile,profile);
1582 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001583 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001584 {
1585 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1586 jpeg_write_marker(jpeg_info,XML_MARKER,
1587 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1588 }
1589 xmp_profile=DestroyStringInfo(xmp_profile);
1590 }
cristye8c25f92010-06-03 00:53:06 +00001591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1592 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001593 name=GetNextImageProfile(image);
1594 }
1595 custom_profile=DestroyStringInfo(custom_profile);
1596}
1597
1598static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1599{
1600 DestinationManager
1601 *destination;
1602
1603 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1604 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1605 destination=(DestinationManager *) cinfo->dest;
1606 destination->manager.init_destination=InitializeDestination;
1607 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1608 destination->manager.term_destination=TerminateDestination;
1609 destination->image=image;
1610}
1611
1612static char **SamplingFactorToList(const char *text)
1613{
1614 char
1615 **textlist;
1616
1617 register char
1618 *q;
1619
1620 register const char
1621 *p;
1622
cristybb503372010-05-27 20:51:26 +00001623 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001624 i;
1625
cristybb503372010-05-27 20:51:26 +00001626 size_t
cristy3ed852e2009-09-05 21:47:34 +00001627 lines;
1628
1629 if (text == (char *) NULL)
1630 return((char **) NULL);
1631 /*
1632 Convert string to an ASCII list.
1633 */
1634 lines=1;
1635 for (p=text; *p != '\0'; p++)
1636 if (*p == ',')
1637 lines++;
1638 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1639 sizeof(*textlist));
1640 if (textlist == (char **) NULL)
1641 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1642 p=text;
cristybb503372010-05-27 20:51:26 +00001643 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001644 {
1645 for (q=(char *) p; *q != '\0'; q++)
1646 if (*q == ',')
1647 break;
1648 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1649 sizeof(*textlist[i]));
1650 if (textlist[i] == (char *) NULL)
1651 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1652 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1653 if (*q == '\r')
1654 q++;
1655 p=q+1;
1656 }
1657 textlist[i]=(char *) NULL;
1658 return(textlist);
1659}
1660
1661static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1662 Image *image)
1663{
1664 const char
1665 *option,
1666 *sampling_factor,
1667 *value;
1668
1669 ErrorManager
1670 error_manager;
1671
1672 JSAMPLE
1673 *jpeg_pixels;
1674
1675 JSAMPROW
1676 scanline[1];
1677
cristybb503372010-05-27 20:51:26 +00001678 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001679 y;
1680
1681 MagickBooleanType
1682 status;
1683
1684 register JSAMPLE
1685 *q;
1686
cristybb503372010-05-27 20:51:26 +00001687 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001688 i;
1689
1690 struct jpeg_compress_struct
1691 jpeg_info;
1692
1693 struct jpeg_error_mgr
1694 jpeg_error;
1695
1696 /*
1697 Open image file.
1698 */
1699 assert(image_info != (const ImageInfo *) NULL);
1700 assert(image_info->signature == MagickSignature);
1701 assert(image != (Image *) NULL);
1702 assert(image->signature == MagickSignature);
1703 if (image->debug != MagickFalse)
1704 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1705 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1706 if (status == MagickFalse)
1707 return(status);
1708 /*
1709 Initialize JPEG parameters.
1710 */
1711 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1712 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1713 jpeg_info.client_data=(void *) image;
1714 jpeg_info.err=jpeg_std_error(&jpeg_error);
1715 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
cristy3ed852e2009-09-05 21:47:34 +00001716 error_manager.image=image;
1717 jpeg_pixels=(JSAMPLE *) NULL;
1718 if (setjmp(error_manager.error_recovery) != 0)
1719 {
1720 jpeg_destroy_compress(&jpeg_info);
1721 (void) CloseBlob(image);
1722 return(MagickFalse);
1723 }
1724 jpeg_info.client_data=(void *) &error_manager;
1725 jpeg_create_compress(&jpeg_info);
1726 JPEGDestinationManager(&jpeg_info,image);
1727 if ((image->columns != (unsigned int) image->columns) ||
1728 (image->rows != (unsigned int) image->rows))
1729 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1730 jpeg_info.image_width=(unsigned int) image->columns;
1731 jpeg_info.image_height=(unsigned int) image->rows;
1732 jpeg_info.input_components=3;
1733 jpeg_info.data_precision=8;
1734 jpeg_info.in_color_space=JCS_RGB;
1735 switch (image->colorspace)
1736 {
1737 case CMYKColorspace:
1738 {
1739 jpeg_info.input_components=4;
1740 jpeg_info.in_color_space=JCS_CMYK;
1741 break;
1742 }
1743 case YCbCrColorspace:
1744 case Rec601YCbCrColorspace:
1745 case Rec709YCbCrColorspace:
1746 {
1747 jpeg_info.in_color_space=JCS_YCbCr;
1748 break;
1749 }
1750 case GRAYColorspace:
1751 case Rec601LumaColorspace:
1752 case Rec709LumaColorspace:
1753 {
1754 jpeg_info.input_components=1;
1755 jpeg_info.in_color_space=JCS_GRAYSCALE;
1756 break;
1757 }
1758 default:
cristy9f396782009-12-21 01:37:45 +00001759 {
1760 if (image->colorspace != RGBColorspace)
1761 (void) TransformImageColorspace(image,RGBColorspace);
cristy3ed852e2009-09-05 21:47:34 +00001762 break;
cristy9f396782009-12-21 01:37:45 +00001763 }
cristy3ed852e2009-09-05 21:47:34 +00001764 }
1765 if ((image_info->type != TrueColorType) &&
1766 (IsGrayImage(image,&image->exception) != MagickFalse))
1767 {
1768 jpeg_info.input_components=1;
1769 jpeg_info.in_color_space=JCS_GRAYSCALE;
1770 }
1771 jpeg_set_defaults(&jpeg_info);
1772 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1773 jpeg_info.data_precision=8;
1774 else
1775 if (sizeof(JSAMPLE) > 1)
1776 jpeg_info.data_precision=12;
1777 jpeg_info.density_unit=(UINT8) 1;
1778 if (image->debug != MagickFalse)
1779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001780 "Image resolution: %.20g,%.20g",floor(image->x_resolution+0.5),
1781 floor(image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001782 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1783 {
1784 /*
1785 Set image resolution.
1786 */
1787 jpeg_info.write_JFIF_header=MagickTrue;
cristyd999ef62010-05-08 19:54:26 +00001788 jpeg_info.X_density=(UINT16) floor(image->x_resolution+0.5);
1789 jpeg_info.Y_density=(UINT16) floor(image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001790 if (image->units == PixelsPerInchResolution)
1791 jpeg_info.density_unit=(UINT8) 1;
1792 if (image->units == PixelsPerCentimeterResolution)
1793 jpeg_info.density_unit=(UINT8) 2;
1794 }
1795 option=GetImageOption(image_info,"jpeg:dct-method");
1796 if (option != (const char *) NULL)
1797 switch (*option)
1798 {
1799 case 'D':
1800 case 'd':
1801 {
1802 if (LocaleCompare(option,"default") == 0)
1803 jpeg_info.dct_method=JDCT_DEFAULT;
1804 break;
1805 }
1806 case 'F':
1807 case 'f':
1808 {
1809 if (LocaleCompare(option,"fastest") == 0)
1810 jpeg_info.dct_method=JDCT_FASTEST;
1811 if (LocaleCompare(option,"float") == 0)
1812 jpeg_info.dct_method=JDCT_FLOAT;
1813 break;
1814 }
1815 case 'I':
1816 case 'i':
1817 {
1818 if (LocaleCompare(option,"ifast") == 0)
1819 jpeg_info.dct_method=JDCT_IFAST;
1820 if (LocaleCompare(option,"islow") == 0)
1821 jpeg_info.dct_method=JDCT_ISLOW;
1822 break;
1823 }
1824 }
1825 option=GetImageOption(image_info,"jpeg:optimize-coding");
1826 if (option != (const char *) NULL)
1827 {
1828 jpeg_info.optimize_coding=MagickFalse;
1829 if (IsMagickTrue(option) != MagickFalse)
1830 jpeg_info.optimize_coding=MagickTrue;
1831 }
1832 else
1833 {
1834 MagickSizeType
1835 length;
1836
1837 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1838 image->rows*sizeof(JSAMPLE);
1839 if (length == (MagickSizeType) ((size_t) length))
1840 {
1841 /*
1842 Perform optimization only if available memory resources permit it.
1843 */
1844 status=AcquireMagickResource(MemoryResource,length);
1845 if (status != MagickFalse)
1846 jpeg_info.optimize_coding=MagickTrue;
1847 RelinquishMagickResource(MemoryResource,length);
1848 }
1849 }
1850#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1851 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1852 (image_info->interlace != NoInterlace))
1853 {
1854 if (image->debug != MagickFalse)
1855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1856 "Interlace: progressive");
1857 jpeg_simple_progression(&jpeg_info);
1858 }
1859 else
1860 if (image->debug != MagickFalse)
1861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 "Interlace: non-progressive");
1863#else
1864 if (image->debug != MagickFalse)
1865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1866 "Interlace: nonprogressive");
1867#endif
cristy0adb4f92009-11-28 18:08:51 +00001868 option=GetImageOption(image_info,"jpeg:extent");
1869 if (option != (const char *) NULL)
1870 {
1871 Image
1872 *jpeg_image;
1873
1874 ImageInfo
1875 *jpeg_info;
1876
1877 jpeg_info=CloneImageInfo(image_info);
1878 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1879 if (jpeg_image != (Image *) NULL)
1880 {
1881 MagickSizeType
1882 extent;
1883
1884 size_t
cristy87e73ab2010-02-12 01:59:12 +00001885 maximum,
1886 minimum;
cristy0adb4f92009-11-28 18:08:51 +00001887
1888 /*
1889 Search for compression quality that does not exceed image extent.
1890 */
1891 jpeg_info->quality=0;
cristyf2f27272009-12-17 14:48:46 +00001892 extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00001893 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1894 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00001895 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00001896 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00001897 {
cristy87e73ab2010-02-12 01:59:12 +00001898 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy0adb4f92009-11-28 18:08:51 +00001899 status=WriteJPEGImage(jpeg_info,jpeg_image);
cristyc8d69b12010-02-13 19:06:26 +00001900 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00001901 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00001902 else
cristy87e73ab2010-02-12 01:59:12 +00001903 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00001904 }
1905 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00001906 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00001907 jpeg_image=DestroyImage(jpeg_image);
1908 }
1909 jpeg_info=DestroyImageInfo(jpeg_info);
1910 }
cristy3ed852e2009-09-05 21:47:34 +00001911 if ((image_info->compression != LosslessJPEGCompression) &&
1912 (image->quality <= 100))
1913 {
1914 if (image->quality == UndefinedCompressionQuality)
1915 jpeg_set_quality(&jpeg_info,92,MagickTrue);
1916 else
1917 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
1918 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
1920 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00001921 }
1922 else
1923 {
1924#if !defined(C_LOSSLESS_SUPPORTED)
1925 jpeg_set_quality(&jpeg_info,100,MagickTrue);
1926 if (image->debug != MagickFalse)
1927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
1928#else
1929 if (image->quality < 100)
1930 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1931 CoderWarning,"LosslessToLossyJPEGConversion",image->filename);
1932 else
1933 {
1934 int
1935 point_transform,
1936 predictor;
1937
1938 predictor=image->quality/100; /* range 1-7 */
1939 point_transform=image->quality % 20; /* range 0-15 */
1940 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
1941 if (image->debug != MagickFalse)
1942 {
1943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1944 "Compression: lossless");
1945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1946 "Predictor: %d",predictor);
1947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1948 "Point Transform: %d",point_transform);
1949 }
1950 }
1951#endif
1952 }
1953 sampling_factor=(const char *) NULL;
1954 value=GetImageProperty(image,"jpeg:sampling-factor");
1955 if (value != (char *) NULL)
1956 {
1957 sampling_factor=value;
1958 if (image->debug != MagickFalse)
1959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1960 " Input sampling-factors=%s",sampling_factor);
1961 }
1962 if (image_info->sampling_factor != (char *) NULL)
1963 sampling_factor=image_info->sampling_factor;
1964 if (sampling_factor == (const char *) NULL)
1965 {
1966 if (image->quality >= 90)
1967 for (i=0; i < MAX_COMPONENTS; i++)
1968 {
1969 jpeg_info.comp_info[i].h_samp_factor=1;
1970 jpeg_info.comp_info[i].v_samp_factor=1;
1971 }
1972 }
1973 else
1974 {
1975 char
1976 **factors;
1977
1978 GeometryInfo
1979 geometry_info;
1980
1981 MagickStatusType
1982 flags;
1983
1984 /*
1985 Set sampling factor.
1986 */
1987 i=0;
1988 factors=SamplingFactorToList(sampling_factor);
1989 if (factors != (char **) NULL)
1990 {
1991 for (i=0; i < MAX_COMPONENTS; i++)
1992 {
1993 if (factors[i] == (char *) NULL)
1994 break;
1995 flags=ParseGeometry(factors[i],&geometry_info);
1996 if ((flags & SigmaValue) == 0)
1997 geometry_info.sigma=geometry_info.rho;
1998 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
1999 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2000 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2001 }
2002 factors=(char **) RelinquishMagickMemory(factors);
2003 }
2004 for ( ; i < MAX_COMPONENTS; i++)
2005 {
2006 jpeg_info.comp_info[i].h_samp_factor=1;
2007 jpeg_info.comp_info[i].v_samp_factor=1;
2008 }
2009 }
2010 if (jpeg_info.input_components == 1)
2011 for (i=0; i < MAX_COMPONENTS; i++)
2012 {
2013 jpeg_info.comp_info[i].h_samp_factor=1;
2014 jpeg_info.comp_info[i].v_samp_factor=1;
2015 }
2016 jpeg_start_compress(&jpeg_info,MagickTrue);
2017 if (image->debug != MagickFalse)
2018 {
2019 if (image->storage_class == PseudoClass)
2020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2021 "Storage class: PseudoClass");
2022 else
2023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2024 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2026 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002027 if (image->colors != 0)
2028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002029 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002030 else
2031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2032 "Number of colors: unspecified");
2033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2034 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2035 switch (image->colorspace)
2036 {
2037 case CMYKColorspace:
2038 {
2039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2040 "Storage class: DirectClass");
2041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2042 "Colorspace: CMYK");
2043 break;
2044 }
2045 case YCbCrColorspace:
2046 case Rec601YCbCrColorspace:
2047 case Rec709YCbCrColorspace:
2048 {
2049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2050 "Colorspace: YCbCr");
2051 break;
2052 }
2053 default:
2054 break;
2055 }
2056 switch (image->colorspace)
2057 {
2058 case CMYKColorspace:
2059 {
2060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2061 "Colorspace: CMYK");
2062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2063 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2064 jpeg_info.comp_info[0].h_samp_factor,
2065 jpeg_info.comp_info[0].v_samp_factor,
2066 jpeg_info.comp_info[1].h_samp_factor,
2067 jpeg_info.comp_info[1].v_samp_factor,
2068 jpeg_info.comp_info[2].h_samp_factor,
2069 jpeg_info.comp_info[2].v_samp_factor,
2070 jpeg_info.comp_info[3].h_samp_factor,
2071 jpeg_info.comp_info[3].v_samp_factor);
2072 break;
2073 }
2074 case GRAYColorspace:
2075 case Rec601LumaColorspace:
2076 case Rec709LumaColorspace:
2077 {
2078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2079 "Colorspace: GRAY");
2080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2081 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2082 jpeg_info.comp_info[0].v_samp_factor);
2083 break;
2084 }
2085 case RGBColorspace:
2086 {
2087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2088 "Image colorspace is RGB");
2089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2090 "Sampling factors: %dx%d,%dx%d,%dx%d",
2091 jpeg_info.comp_info[0].h_samp_factor,
2092 jpeg_info.comp_info[0].v_samp_factor,
2093 jpeg_info.comp_info[1].h_samp_factor,
2094 jpeg_info.comp_info[1].v_samp_factor,
2095 jpeg_info.comp_info[2].h_samp_factor,
2096 jpeg_info.comp_info[2].v_samp_factor);
2097 break;
2098 }
2099 case YCbCrColorspace:
2100 case Rec601YCbCrColorspace:
2101 case Rec709YCbCrColorspace:
2102 {
2103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2104 "Colorspace: YCbCr");
2105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2106 "Sampling factors: %dx%d,%dx%d,%dx%d",
2107 jpeg_info.comp_info[0].h_samp_factor,
2108 jpeg_info.comp_info[0].v_samp_factor,
2109 jpeg_info.comp_info[1].h_samp_factor,
2110 jpeg_info.comp_info[1].v_samp_factor,
2111 jpeg_info.comp_info[2].h_samp_factor,
2112 jpeg_info.comp_info[2].v_samp_factor);
2113 break;
2114 }
2115 default:
2116 {
2117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2118 image->colorspace);
2119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2120 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2121 jpeg_info.comp_info[0].h_samp_factor,
2122 jpeg_info.comp_info[0].v_samp_factor,
2123 jpeg_info.comp_info[1].h_samp_factor,
2124 jpeg_info.comp_info[1].v_samp_factor,
2125 jpeg_info.comp_info[2].h_samp_factor,
2126 jpeg_info.comp_info[2].v_samp_factor,
2127 jpeg_info.comp_info[3].h_samp_factor,
2128 jpeg_info.comp_info[3].v_samp_factor);
2129 break;
2130 }
2131 }
2132 }
2133 /*
2134 Write JPEG profiles.
2135 */
2136 value=GetImageProperty(image,"comment");
2137 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002138 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002139 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2140 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2141 if (image->profiles != (void *) NULL)
2142 WriteProfile(&jpeg_info,image);
2143 /*
2144 Convert MIFF to JPEG raster pixels.
2145 */
2146 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2147 jpeg_info.input_components*sizeof(*jpeg_pixels));
2148 if (jpeg_pixels == (JSAMPLE *) NULL)
2149 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2150 if (setjmp(error_manager.error_recovery) != 0)
2151 {
2152 jpeg_destroy_compress(&jpeg_info);
2153 if (jpeg_pixels != (unsigned char *) NULL)
2154 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2155 (void) CloseBlob(image);
2156 return(MagickFalse);
2157 }
2158 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002159 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002160 {
cristy3ed852e2009-09-05 21:47:34 +00002161 if ((jpeg_info.in_color_space == JCS_RGB) ||
2162 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002163 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002164 {
2165 register const PixelPacket
2166 *p;
2167
cristybb503372010-05-27 20:51:26 +00002168 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002169 x;
2170
2171 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2172 if (p == (const PixelPacket *) NULL)
2173 break;
2174 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002175 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002176 {
cristyce70c172010-01-07 17:15:30 +00002177 *q++=(JSAMPLE) ScaleQuantumToChar(GetRedPixelComponent(p));
2178 *q++=(JSAMPLE) ScaleQuantumToChar(GetGreenPixelComponent(p));
2179 *q++=(JSAMPLE) ScaleQuantumToChar(GetBluePixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00002180 p++;
2181 }
2182 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002183 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002184 break;
2185 }
2186 else
cristye90d7402010-03-14 18:21:29 +00002187 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002188 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002189 {
2190 register const PixelPacket
2191 *p;
2192
cristybb503372010-05-27 20:51:26 +00002193 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002194 x;
2195
2196 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2197 if (p == (const PixelPacket *) NULL)
2198 break;
2199 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002200 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002201 {
2202 *q++=(JSAMPLE) ScaleQuantumToChar(PixelIntensityToQuantum(p));
2203 p++;
2204 }
2205 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002206 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002207 break;
2208 }
2209 else
cristybb503372010-05-27 20:51:26 +00002210 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002211 {
2212 register const IndexPacket
2213 *indexes;
2214
2215 register const PixelPacket
2216 *p;
2217
cristybb503372010-05-27 20:51:26 +00002218 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002219 x;
2220
2221 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2222 if (p == (const PixelPacket *) NULL)
2223 break;
2224 q=jpeg_pixels;
2225 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00002226 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002227 {
2228 /*
2229 Convert DirectClass packets to contiguous CMYK scanlines.
2230 */
2231 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2232 GetRedPixelComponent(p))));
2233 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2234 GetGreenPixelComponent(p))));
2235 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2236 GetBluePixelComponent(p))));
2237 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2238 indexes[x])));
2239 p++;
2240 }
2241 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002242 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002243 break;
2244 }
2245 }
2246 else
2247 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002248 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002249 {
2250 register const PixelPacket
2251 *p;
2252
cristybb503372010-05-27 20:51:26 +00002253 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002254 x;
2255
2256 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2257 if (p == (const PixelPacket *) NULL)
2258 break;
2259 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002260 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002261 {
2262 *q++=(JSAMPLE) (ScaleQuantumToShort(PixelIntensityToQuantum(p)) >>
2263 4);
2264 p++;
2265 }
2266 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002267 if (SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002268 break;
2269 }
2270 else
2271 if ((jpeg_info.in_color_space == JCS_RGB) ||
2272 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002273 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002274 {
2275 register const PixelPacket
2276 *p;
2277
cristybb503372010-05-27 20:51:26 +00002278 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002279 x;
2280
2281 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2282 if (p == (const PixelPacket *) NULL)
2283 break;
2284 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002285 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002286 {
2287 *q++=(JSAMPLE) (ScaleQuantumToShort(GetRedPixelComponent(p)) >>
2288 4);
2289 *q++=(JSAMPLE) (ScaleQuantumToShort(GetGreenPixelComponent(p)) >>
2290 4);
2291 *q++=(JSAMPLE) (ScaleQuantumToShort(GetBluePixelComponent(p)) >>
2292 4);
2293 p++;
2294 }
2295 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002296 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2297 image->rows);
cristye90d7402010-03-14 18:21:29 +00002298 if (status == MagickFalse)
2299 break;
2300 }
2301 else
cristybb503372010-05-27 20:51:26 +00002302 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002303 {
2304 register const IndexPacket
2305 *indexes;
2306
2307 register const PixelPacket
2308 *p;
2309
cristybb503372010-05-27 20:51:26 +00002310 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002311 x;
2312
2313 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2314 if (p == (const PixelPacket *) NULL)
2315 break;
2316 q=jpeg_pixels;
2317 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00002318 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002319 {
2320 /*
2321 Convert DirectClass packets to contiguous CMYK scanlines.
2322 */
cristye90d7402010-03-14 18:21:29 +00002323 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2324 GetRedPixelComponent(p)) >> 4));
2325 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2326 GetGreenPixelComponent(p)) >> 4));
2327 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2328 GetBluePixelComponent(p)) >> 4));
2329 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(indexes[x]) >> 4));
cristy3ed852e2009-09-05 21:47:34 +00002330 p++;
2331 }
2332 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002333 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2334 image->rows);
cristye90d7402010-03-14 18:21:29 +00002335 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002336 break;
2337 }
cristybb503372010-05-27 20:51:26 +00002338 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002339 jpeg_finish_compress(&jpeg_info);
2340 /*
2341 Relinquish resources.
2342 */
2343 jpeg_destroy_compress(&jpeg_info);
2344 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2345 (void) CloseBlob(image);
2346 return(MagickTrue);
2347}
2348#endif