blob: 9df4e8e9d15d03f611d5b7364c9ed02ddee3be2a [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% %
20% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21% 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"
53#include "magick/colorspace.h"
54#include "magick/constitute.h"
55#include "magick/exception.h"
56#include "magick/exception-private.h"
57#include "magick/geometry.h"
58#include "magick/image.h"
59#include "magick/image-private.h"
60#include "magick/list.h"
61#include "magick/log.h"
62#include "magick/magick.h"
63#include "magick/memory_.h"
64#include "magick/monitor.h"
65#include "magick/monitor-private.h"
66#include "magick/option.h"
67#include "magick/profile.h"
68#include "magick/property.h"
69#include "magick/quantum-private.h"
70#include "magick/resource_.h"
71#include "magick/splay-tree.h"
72#include "magick/static.h"
73#include "magick/string_.h"
74#include "magick/module.h"
75#include "magick/utility.h"
76#include <setjmp.h>
77#if defined(MAGICKCORE_JPEG_DELEGATE)
78#define JPEG_INTERNAL_OPTIONS
79#if defined(__MINGW32__)
80# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
81#endif
82#undef HAVE_STDLIB_H
83#include "jpeglib.h"
84#include "jerror.h"
85#endif
86
87/*
88 Define declarations.
89*/
90#define ICC_MARKER (JPEG_APP0+2)
91#define ICC_PROFILE "ICC_PROFILE"
92#define IPTC_MARKER (JPEG_APP0+13)
93#define XML_MARKER (JPEG_APP0+1)
94#define MaxBufferExtent 8192
95
96/*
97 Typedef declarations.
98*/
99#if defined(MAGICKCORE_JPEG_DELEGATE)
100typedef struct _DestinationManager
101{
102 struct jpeg_destination_mgr
103 manager;
104
105 Image
106 *image;
107
108 JOCTET
109 *buffer;
110} DestinationManager;
111
112typedef struct _ErrorManager
113{
114 Image
115 *image;
116
117 jmp_buf
118 error_recovery;
119} ErrorManager;
120
121typedef struct _SourceManager
122{
123 struct jpeg_source_mgr
124 manager;
125
126 Image
127 *image;
128
129 JOCTET
130 *buffer;
131
132 boolean
133 start_of_blob;
134} SourceManager;
135#endif
136
137/*
138 Forward declarations.
139*/
140#if defined(MAGICKCORE_JPEG_DELEGATE)
141static MagickBooleanType
142 WriteJPEGImage(const ImageInfo *,Image *);
143#endif
144
145/*
146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147% %
148% %
149% %
150% I s J P E G %
151% %
152% %
153% %
154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155%
156% IsJPEG() returns MagickTrue if the image format type, identified by the
157% magick string, is JPEG.
158%
159% The format of the IsJPEG method is:
160%
161% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
162%
163% A description of each parameter follows:
164%
165% o magick: compare image format pattern against these bytes.
166%
167% o length: Specifies the length of the magick string.
168%
169*/
170static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
171{
172 if (length < 3)
173 return(MagickFalse);
174 if (memcmp(magick,"\377\330\377",3) == 0)
175 return(MagickTrue);
176 return(MagickFalse);
177}
178
179#if defined(MAGICKCORE_JPEG_DELEGATE)
180/*
181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182% %
183% %
184% %
185% R e a d J P E G I m a g e %
186% %
187% %
188% %
189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190%
191% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
192% the memory necessary for the new Image structure and returns a pointer to
193% the new image.
194%
195% The format of the ReadJPEGImage method is:
196%
197% Image *ReadJPEGImage(const ImageInfo *image_info,
198% ExceptionInfo *exception)
199%
200% A description of each parameter follows:
201%
202% o image_info: the image info.
203%
204% o exception: return any errors or warnings in this structure.
205%
206*/
207
208static MagickBooleanType EmitMessage(j_common_ptr jpeg_info,int level)
209{
210 char
211 message[JMSG_LENGTH_MAX];
212
213 ErrorManager
214 *error_manager;
215
216 Image
217 *image;
218
219 (jpeg_info->err->format_message)(jpeg_info,message);
220 error_manager=(ErrorManager *) jpeg_info->client_data;
221 image=error_manager->image;
222 if (level < 0)
223 {
224 if ((jpeg_info->err->num_warnings == 0) ||
225 (jpeg_info->err->trace_level >= 3))
226 ThrowBinaryException(CorruptImageWarning,(char *) message,
227 image->filename);
228 jpeg_info->err->num_warnings++;
229 }
230 else
231 if (jpeg_info->err->trace_level >= level)
232 ThrowBinaryException(CoderError,(char *) message,image->filename);
233 return(MagickTrue);
234}
235
236static boolean FillInputBuffer(j_decompress_ptr cinfo)
237{
238 SourceManager
239 *source;
240
241 source=(SourceManager *) cinfo->src;
242 source->manager.bytes_in_buffer=(size_t)
243 ReadBlob(source->image,MaxBufferExtent,source->buffer);
244 if (source->manager.bytes_in_buffer == 0)
245 {
246 if (source->start_of_blob != 0)
247 ERREXIT(cinfo,JERR_INPUT_EMPTY);
248 WARNMS(cinfo,JWRN_JPEG_EOF);
249 source->buffer[0]=(JOCTET) 0xff;
250 source->buffer[1]=(JOCTET) JPEG_EOI;
251 source->manager.bytes_in_buffer=2;
252 }
253 source->manager.next_input_byte=source->buffer;
254 source->start_of_blob=FALSE;
255 return(TRUE);
256}
257
258static int GetCharacter(j_decompress_ptr jpeg_info)
259{
260 if (jpeg_info->src->bytes_in_buffer == 0)
261 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
262 jpeg_info->src->bytes_in_buffer--;
263 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
264}
265
266static void InitializeSource(j_decompress_ptr cinfo)
267{
268 SourceManager
269 *source;
270
271 source=(SourceManager *) cinfo->src;
272 source->start_of_blob=TRUE;
273}
274
cristye8dd1302009-11-11 02:45:03 +0000275static MagickBooleanType IsITUFaxImage(const Image *image)
276{
277 const StringInfo
278 *profile;
279
280 const unsigned char
281 *datum;
282
cristyace6aa42009-11-11 03:17:33 +0000283 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000284 if (profile == (const StringInfo *) NULL)
285 return(MagickFalse);
286 if (GetStringInfoLength(profile) < 5)
287 return(MagickFalse);
288 datum=GetStringInfoDatum(profile);
289 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
290 (datum[3] == 0x41) && (datum[4] == 0x58))
291 return(MagickTrue);
292 return(MagickFalse);
293}
294
cristy3ed852e2009-09-05 21:47:34 +0000295static void JPEGErrorHandler(j_common_ptr jpeg_info)
296{
297 ErrorManager
298 *error_manager;
299
300 (void) EmitMessage(jpeg_info,0);
301 error_manager=(ErrorManager *) jpeg_info->client_data;
302 longjmp(error_manager->error_recovery,1);
303}
304
305static boolean ReadComment(j_decompress_ptr jpeg_info)
306{
307 char
308 *comment;
309
310 ErrorManager
311 *error_manager;
312
313 Image
314 *image;
315
316 register char
317 *p;
318
319 register long
320 i;
321
322 size_t
323 length;
324
325 /*
326 Determine length of comment.
327 */
328 error_manager=(ErrorManager *) jpeg_info->client_data;
329 image=error_manager->image;
330 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8);
331 length+=GetCharacter(jpeg_info);
332 length-=2;
333 if (length <= 0)
334 return(MagickTrue);
335 comment=(char *) NULL;
336 if (~length >= MaxTextExtent)
337 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
338 sizeof(*comment));
339 if (comment == (char *) NULL)
340 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
341 image->filename);
342 /*
343 Read comment.
344 */
345 i=(long) length-1;
346 for (p=comment; i-- >= 0; p++)
347 *p=(char) GetCharacter(jpeg_info);
348 *p='\0';
349 (void) SetImageProperty(image,"comment",comment);
350 comment=DestroyString(comment);
351 return(MagickTrue);
352}
353
354static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
355{
356 char
357 magick[12];
358
359 ErrorManager
360 *error_manager;
361
362 Image
363 *image;
364
365 MagickBooleanType
366 status;
367
368 register long
369 i;
370
371 register unsigned char
372 *p;
373
374 size_t
375 length;
376
377 StringInfo
378 *icc_profile,
379 *profile;
380
381 /*
382 Read color profile.
383 */
384 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8);
385 length+=(size_t) GetCharacter(jpeg_info);
386 length-=2;
387 if (length <= 14)
388 {
389 while (length-- > 0)
390 (void) GetCharacter(jpeg_info);
391 return(MagickTrue);
392 }
393 for (i=0; i < 12; i++)
394 magick[i]=(char) GetCharacter(jpeg_info);
395 if (LocaleCompare(magick,ICC_PROFILE) != 0)
396 {
397 /*
398 Not a ICC profile, return.
399 */
400 for (i=0; i < (long) (length-12); i++)
401 (void) GetCharacter(jpeg_info);
402 return(MagickTrue);
403 }
404 (void) GetCharacter(jpeg_info); /* id */
405 (void) GetCharacter(jpeg_info); /* markers */
406 length-=14;
407 error_manager=(ErrorManager *) jpeg_info->client_data;
408 image=error_manager->image;
409 profile=AcquireStringInfo(length);
410 if (profile == (StringInfo *) NULL)
411 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
412 image->filename);
413 p=GetStringInfoDatum(profile);
414 for (i=(long) GetStringInfoLength(profile)-1; i >= 0; i--)
415 *p++=(unsigned char) GetCharacter(jpeg_info);
416 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
417 if (icc_profile != (StringInfo *) NULL)
418 {
419 ConcatenateStringInfo(icc_profile,profile);
420 profile=DestroyStringInfo(profile);
421 }
422 else
423 {
424 status=SetImageProfile(image,"icc",profile);
425 profile=DestroyStringInfo(profile);
426 if (status == MagickFalse)
427 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
428 image->filename);
429 }
430 if (image->debug != MagickFalse)
431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
432 "Profile: ICC, %lu bytes",(unsigned long) length);
433 return(MagickTrue);
434}
435
436static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
437{
438 char
439 magick[MaxTextExtent];
440
441 ErrorManager
442 *error_manager;
443
444 Image
445 *image;
446
447 MagickBooleanType
448 status;
449
450 register long
451 i;
452
453 register unsigned char
454 *p;
455
456 size_t
457 length;
458
459 StringInfo
460 *iptc_profile,
461 *profile;
462
463 /*
464 Determine length of binary data stored here.
465 */
466 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8);
467 length+=(size_t) GetCharacter(jpeg_info);
468 length-=2;
469 if (length <= 14)
470 {
471 while (length-- > 0)
472 (void) GetCharacter(jpeg_info);
473 return(MagickTrue);
474 }
475 /*
476 Validate that this was written as a Photoshop resource format slug.
477 */
478 for (i=0; i < 10; i++)
479 magick[i]=(char) GetCharacter(jpeg_info);
480 magick[10]='\0';
481 if (length <= 10)
482 return(MagickTrue);
483 length-=10;
484 if (LocaleCompare(magick,"Photoshop ") != 0)
485 {
486 /*
487 Not a IPTC profile, return.
488 */
489 for (i=0; i < (long) length; i++)
490 (void) GetCharacter(jpeg_info);
491 return(MagickTrue);
492 }
493 /*
494 Remove the version number.
495 */
496 for (i=0; i < 4; i++)
497 (void) GetCharacter(jpeg_info);
498 if (length <= 4)
499 return(MagickTrue);
500 length-=4;
501 if (length == 0)
502 return(MagickTrue);
503 error_manager=(ErrorManager *) jpeg_info->client_data;
504 image=error_manager->image;
505 profile=AcquireStringInfo(length);
506 if (profile == (StringInfo *) NULL)
507 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
508 image->filename);
509 p=GetStringInfoDatum(profile);
510 for (i=(long) GetStringInfoLength(profile)-1; i >= 0; i--)
511 *p++=(unsigned char) GetCharacter(jpeg_info);
512 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
513 if (iptc_profile != (StringInfo *) NULL)
514 {
515 ConcatenateStringInfo(iptc_profile,profile);
516 profile=DestroyStringInfo(profile);
517 }
518 else
519 {
520 status=SetImageProfile(image,"8bim",profile);
521 profile=DestroyStringInfo(profile);
522 if (status == MagickFalse)
523 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
524 image->filename);
525 }
526 if (image->debug != MagickFalse)
527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
528 "Profile: iptc, %lu bytes",(unsigned long) length);
529 return(MagickTrue);
530}
531
532static boolean ReadProfile(j_decompress_ptr jpeg_info)
533{
534 char
535 name[MaxTextExtent];
536
537 ErrorManager
538 *error_manager;
539
540 Image
541 *image;
542
543 int
544 marker;
545
546 MagickBooleanType
547 status;
548
549 register long
550 i;
551
552 register unsigned char
553 *p;
554
555 size_t
556 length;
557
558 StringInfo
559 *profile;
560
561 /*
562 Read generic profile.
563 */
564 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8);
565 length+=(size_t) GetCharacter(jpeg_info);
566 if (length <= 2)
567 return(MagickTrue);
568 length-=2;
569 marker=jpeg_info->unread_marker-JPEG_APP0;
570 (void) FormatMagickString(name,MaxTextExtent,"APP%d",marker);
571 error_manager=(ErrorManager *) jpeg_info->client_data;
572 image=error_manager->image;
573 profile=AcquireStringInfo(length);
574 if (profile == (StringInfo *) NULL)
575 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
576 image->filename);
577 p=GetStringInfoDatum(profile);
578 for (i=(long) GetStringInfoLength(profile)-1; i >= 0; i--)
579 *p++=(unsigned char) GetCharacter(jpeg_info);
580 if (marker == 1)
581 {
582 p=GetStringInfoDatum(profile);
583 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
584 (void) CopyMagickString(name,"exif",MaxTextExtent);
585 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
586 {
587 long
588 j;
589
590 /*
591 Extract namespace from XMP profile.
592 */
593 p=GetStringInfoDatum(profile);
594 for (j=0; j < (long) GetStringInfoLength(profile); j++)
595 {
596 if (*p == '\0')
597 break;
598 p++;
599 }
600 if (j < (long) GetStringInfoLength(profile))
601 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
602 (void) CopyMagickString(name,"xmp",MaxTextExtent);
603 }
604 }
605 status=SetImageProfile(image,name,profile);
606 profile=DestroyStringInfo(profile);
607 if (status == MagickFalse)
608 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
609 image->filename);
610 if (image->debug != MagickFalse)
611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
612 "Profile: %s, %lu bytes",name,(unsigned long) length);
613 return(MagickTrue);
614}
615
616static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
617{
618 SourceManager
619 *source;
620
621 if (number_bytes <= 0)
622 return;
623 source=(SourceManager *) cinfo->src;
624 while (number_bytes > (long) source->manager.bytes_in_buffer)
625 {
626 number_bytes-=(long) source->manager.bytes_in_buffer;
627 (void) FillInputBuffer(cinfo);
628 }
629 source->manager.next_input_byte+=(size_t) number_bytes;
630 source->manager.bytes_in_buffer-=(size_t) number_bytes;
631}
632
633static void TerminateSource(j_decompress_ptr cinfo)
634{
635 cinfo=cinfo;
636}
637
638static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
639{
640 SourceManager
641 *source;
642
643 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
644 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
645 source=(SourceManager *) cinfo->src;
646 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
647 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
648 source=(SourceManager *) cinfo->src;
649 source->manager.init_source=InitializeSource;
650 source->manager.fill_input_buffer=FillInputBuffer;
651 source->manager.skip_input_data=SkipInputData;
652 source->manager.resync_to_restart=jpeg_resync_to_restart;
653 source->manager.term_source=TerminateSource;
654 source->manager.bytes_in_buffer=0;
655 source->manager.next_input_byte=NULL;
656 source->image=image;
657}
658
659static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
660 Image *image)
661{
662 image->quality=UndefinedCompressionQuality;
663#if defined(D_PROGRESSIVE_SUPPORTED)
664 if (image->compression == LosslessJPEGCompression)
665 {
666 image->quality=100;
667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
668 "Quality: 100 (lossless)");
669 }
670 else
671#endif
672 {
673 long
674 j,
675 qvalue,
676 sum;
677
678 register long
679 i;
680
681 /*
682 Determine the JPEG compression quality from the quantization tables.
683 */
684 sum=0;
685 for (i=0; i < NUM_QUANT_TBLS; i++)
686 {
687 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
688 for (j=0; j < DCTSIZE2; j++)
689 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
690 }
691 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
692 (jpeg_info->quant_tbl_ptrs[1] != NULL))
693 {
694 long
695 hash[101] =
696 {
697 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
698 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
699 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
700 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
701 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
702 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
703 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
704 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
705 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
706 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
707 0
708 },
709 sums[101] =
710 {
711 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
712 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
713 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
714 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
715 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
716 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
717 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
718 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
719 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
720 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
721 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
722 128, 0
723 };
724
725 qvalue=(long) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
726 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
727 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
728 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
729 for (i=0; i < 100; i++)
730 {
731 if ((qvalue < hash[i]) && (sum < sums[i]))
732 continue;
733 if ((qvalue <= hash[i]) && (sum <= sums[i]))
734 image->quality=(unsigned long) i+1;
735 if (image->debug != MagickFalse)
736 {
737 if (image->quality != UndefinedCompressionQuality)
738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
739 "Quality: %ld",image->quality);
740 else
741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
742 "Quality: %ld (approximate)",i+1);
743 }
744 break;
745 }
746 }
747 else
748 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
749 {
750 long
751 hash[101] =
752 {
753 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
754 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
755 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
756 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
757 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
758 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
759 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
760 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
761 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
762 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
763 0
764 },
765 sums[101] =
766 {
767 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
768 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
769 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
770 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
771 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
772 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
773 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
774 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
775 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
776 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
777 667, 592, 518, 441, 369, 292, 221, 151, 86,
778 64, 0
779 };
780
781 qvalue=(long) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
782 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
783 for (i=0; i < 100; i++)
784 {
785 if ((qvalue < hash[i]) && (sum < sums[i]))
786 continue;
787 image->quality=(unsigned long) i+1;
788 if (image->debug != MagickFalse)
789 {
790 if ((qvalue > hash[i]) || (sum > sums[i]))
791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
792 "Quality: %ld (approximate)",i+1);
793 else
794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
795 "Quality: %ld",i+1);
796 }
797 break;
798 }
799 }
800 }
801}
802
803static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image)
804{
805 char
806 sampling_factor[MaxTextExtent];
807
808 switch (jpeg_info->out_color_space)
809 {
810 case JCS_CMYK:
811 {
812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
813 (void) FormatMagickString(sampling_factor,MaxTextExtent,
814 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
815 jpeg_info->comp_info[0].v_samp_factor,
816 jpeg_info->comp_info[1].h_samp_factor,
817 jpeg_info->comp_info[1].v_samp_factor,
818 jpeg_info->comp_info[2].h_samp_factor,
819 jpeg_info->comp_info[2].v_samp_factor,
820 jpeg_info->comp_info[3].h_samp_factor,
821 jpeg_info->comp_info[3].v_samp_factor);
822 break;
823 }
824 case JCS_GRAYSCALE:
825 {
826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
827 "Colorspace: GRAYSCALE");
828 (void) FormatMagickString(sampling_factor,MaxTextExtent,"%dx%d",
829 jpeg_info->comp_info[0].h_samp_factor,
830 jpeg_info->comp_info[0].v_samp_factor);
831 break;
832 }
833 case JCS_RGB:
834 {
835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
836 (void) FormatMagickString(sampling_factor,MaxTextExtent,
837 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
838 jpeg_info->comp_info[0].v_samp_factor,
839 jpeg_info->comp_info[1].h_samp_factor,
840 jpeg_info->comp_info[1].v_samp_factor,
841 jpeg_info->comp_info[2].h_samp_factor,
842 jpeg_info->comp_info[2].v_samp_factor);
843 break;
844 }
845 default:
846 {
847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
848 jpeg_info->out_color_space);
849 (void) FormatMagickString(sampling_factor,MaxTextExtent,
850 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
851 jpeg_info->comp_info[0].v_samp_factor,
852 jpeg_info->comp_info[1].h_samp_factor,
853 jpeg_info->comp_info[1].v_samp_factor,
854 jpeg_info->comp_info[2].h_samp_factor,
855 jpeg_info->comp_info[2].v_samp_factor,
856 jpeg_info->comp_info[3].h_samp_factor,
857 jpeg_info->comp_info[3].v_samp_factor);
858 break;
859 }
860 }
861 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor);
862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
863 "Sampling Factors: %s",sampling_factor);
864}
865
866static Image *ReadJPEGImage(const ImageInfo *image_info,
867 ExceptionInfo *exception)
868{
869 char
870 value[MaxTextExtent];
871
cristycaec74e2009-09-14 02:20:04 +0000872 const char
cristy11151212009-09-14 13:10:15 +0000873 *option;
cristycaec74e2009-09-14 02:20:04 +0000874
cristy3ed852e2009-09-05 21:47:34 +0000875 ErrorManager
876 error_manager;
877
878 IndexPacket
879 jindex;
880
881 Image
882 *image;
883
884 long
885 y;
886
887 JSAMPLE
888 *jpeg_pixels;
889
890 JSAMPROW
891 scanline[1];
892
893 MagickBooleanType
894 debug,
895 status;
896
897 MagickSizeType
898 number_pixels;
899
900 register long
901 i;
902
903 struct jpeg_decompress_struct
904 jpeg_info;
905
906 struct jpeg_error_mgr
907 jpeg_error;
908
909 register JSAMPLE
910 *p;
911
912 unsigned long
913 precision,
914 units;
915
916 /*
917 Open image file.
918 */
919 assert(image_info != (const ImageInfo *) NULL);
920 assert(image_info->signature == MagickSignature);
921 if (image_info->debug != MagickFalse)
922 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
923 image_info->filename);
924 assert(exception != (ExceptionInfo *) NULL);
925 assert(exception->signature == MagickSignature);
926 debug=IsEventLogging();
927 image=AcquireImage(image_info);
928 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
929 if (status == MagickFalse)
930 {
931 image=DestroyImageList(image);
932 return((Image *) NULL);
933 }
934 /*
935 Initialize JPEG parameters.
936 */
937 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
938 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
939 jpeg_info.err=jpeg_std_error(&jpeg_error);
940 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
941 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
942 jpeg_pixels=(JSAMPLE *) NULL;
943 error_manager.image=image;
944 if (setjmp(error_manager.error_recovery) != 0)
945 {
946 jpeg_destroy_decompress(&jpeg_info);
947 (void) CloseBlob(image);
948 number_pixels=(MagickSizeType) image->columns*image->rows;
949 if (number_pixels != 0)
950 return(GetFirstImageInList(image));
cristy195f6d12009-11-05 18:16:03 +0000951 InheritException(exception,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +0000952 return(DestroyImage(image));
953 }
954 jpeg_info.client_data=(void *) &error_manager;
955 jpeg_create_decompress(&jpeg_info);
956 JPEGSourceManager(&jpeg_info,image);
957 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
958 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
959 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
960 for (i=1; i < 16; i++)
961 if ((i != 2) && (i != 13) && (i != 14))
962 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
963 i=jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +0000964 if ((image_info->colorspace == YCbCrColorspace) ||
965 (image_info->colorspace == Rec601YCbCrColorspace) ||
966 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +0000967 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +0000968 if (IsITUFaxImage(image) != MagickFalse)
969 {
970 image->colorspace=LabColorspace;
971 jpeg_info.out_color_space=JCS_YCbCr;
972 }
973 else
974 if (jpeg_info.out_color_space == JCS_CMYK)
975 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000976 /*
977 Set image resolution.
978 */
979 units=0;
980 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
981 (jpeg_info.Y_density != 1))
982 {
983 image->x_resolution=(double) jpeg_info.X_density;
984 image->y_resolution=(double) jpeg_info.Y_density;
985 units=(unsigned long) jpeg_info.density_unit;
986 }
987 if (units == 1)
988 image->units=PixelsPerInchResolution;
989 if (units == 2)
990 image->units=PixelsPerCentimeterResolution;
991 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +0000992 option=GetImageOption(image_info,"jpeg:size");
993 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000994 {
995 double
996 scale_factor;
997
cristycaec74e2009-09-14 02:20:04 +0000998 GeometryInfo
999 geometry_info;
1000
cristy0adb4f92009-11-28 18:08:51 +00001001 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001002 flags;
1003
cristy3ed852e2009-09-05 21:47:34 +00001004 /*
cristycaec74e2009-09-14 02:20:04 +00001005 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001006 */
cristy11151212009-09-14 13:10:15 +00001007 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001008 if ((flags & SigmaValue) == 0)
1009 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001010 jpeg_calc_output_dimensions(&jpeg_info);
1011 image->magick_columns=jpeg_info.output_width;
1012 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001013 scale_factor=1.0;
1014 if (geometry_info.rho != 0.0)
1015 scale_factor=jpeg_info.output_width/geometry_info.rho;
1016 if ((geometry_info.sigma != 0.0) &&
1017 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1018 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001019 jpeg_info.scale_num=1U;
1020 jpeg_info.scale_denom=(unsigned int) scale_factor;
1021 jpeg_calc_output_dimensions(&jpeg_info);
1022 if (image->debug != MagickFalse)
1023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Scale factor: %ld",
1024 (long) scale_factor);
1025 }
1026 precision=(unsigned long) jpeg_info.data_precision;
1027#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1028#if defined(D_LOSSLESS_SUPPORTED)
1029 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1030 JPEGInterlace : NoInterlace;
1031 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1032 LosslessJPEGCompression : JPEGCompression;
1033 if (jpeg_info.data_precision > 8)
1034 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1035 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1036 image->filename);
1037 if (jpeg_info.data_precision == 16)
1038 jpeg_info.data_precision=12;
1039#else
1040 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1041 NoInterlace;
1042 image->compression=JPEGCompression;
1043#endif
1044#else
1045 image->compression=JPEGCompression;
1046 image->interlace=JPEGInterlace;
1047#endif
1048 if ((image_info->colors > 8) && (image_info->colors <= 256))
1049 {
1050 /*
1051 Let the JPEG library quantize for us.
1052 */
1053 jpeg_info.quantize_colors=MagickTrue;
1054 jpeg_info.desired_number_of_colors=(int) image_info->colors;
1055 }
cristy1b8d4462009-10-27 13:46:56 +00001056 option=GetImageOption(image_info,"jpeg:block-smoothing");
1057 if (option != (const char *) NULL)
1058 {
1059 jpeg_info.do_block_smoothing=MagickFalse;
1060 if (IsMagickTrue(option) != MagickFalse)
1061 jpeg_info.do_block_smoothing=MagickTrue;
1062 }
1063 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1064 if (option != (const char *) NULL)
1065 {
1066 jpeg_info.do_fancy_upsampling=MagickFalse;
1067 if (IsMagickTrue(option) != MagickFalse)
1068 jpeg_info.do_fancy_upsampling=MagickTrue;
1069 }
cristy3ed852e2009-09-05 21:47:34 +00001070 (void) jpeg_start_decompress(&jpeg_info);
1071 image->columns=jpeg_info.output_width;
1072 image->rows=jpeg_info.output_height;
1073 image->depth=(unsigned long) jpeg_info.data_precision;
1074 if (jpeg_info.out_color_space == JCS_YCbCr)
1075 image->colorspace=YCbCrColorspace;
1076 if (jpeg_info.out_color_space == JCS_CMYK)
1077 image->colorspace=CMYKColorspace;
1078 if ((image_info->colors != 0) && (image_info->colors <= 256))
1079 if (AcquireImageColormap(image,image_info->colors) == MagickFalse)
1080 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1081 if ((jpeg_info.output_components == 1) &&
1082 (jpeg_info.quantize_colors == MagickFalse))
1083 {
1084 unsigned long
1085 colors;
1086
1087 colors=(unsigned long) GetQuantumRange(image->depth)+1;
1088 if (AcquireImageColormap(image,colors) == MagickFalse)
1089 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1090 }
1091 if (image->debug != MagickFalse)
1092 {
1093 if (image->interlace != NoInterlace)
1094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1095 "Interlace: progressive");
1096 else
1097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1098 "Interlace: nonprogressive");
1099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1100 (int) jpeg_info.data_precision);
1101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1102 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1103 }
1104 JPEGSetImageQuality(&jpeg_info,image);
1105 JPEGSetImageSamplingFactor(&jpeg_info,image);
1106 (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
1107 jpeg_info.out_color_space);
1108 (void) SetImageProperty(image,"jpeg:colorspace",value);
1109 if (image_info->ping != MagickFalse)
1110 {
1111 jpeg_destroy_decompress(&jpeg_info);
1112 (void) CloseBlob(image);
1113 return(GetFirstImageInList(image));
1114 }
1115 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1116 jpeg_info.output_components*sizeof(JSAMPLE));
1117 if (jpeg_pixels == (JSAMPLE *) NULL)
1118 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1119 /*
1120 Convert JPEG pixels to pixel packets.
1121 */
1122 if (setjmp(error_manager.error_recovery) != 0)
1123 {
1124 if (jpeg_pixels != (unsigned char *) NULL)
1125 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1126 jpeg_destroy_decompress(&jpeg_info);
1127 (void) CloseBlob(image);
1128 number_pixels=(MagickSizeType) image->columns*image->rows;
1129 if (number_pixels != 0)
1130 return(GetFirstImageInList(image));
1131 return(DestroyImage(image));
1132 }
1133 if (jpeg_info.quantize_colors != MagickFalse)
1134 {
1135 image->colors=(unsigned long) jpeg_info.actual_number_of_colors;
1136 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
1137 for (i=0; i < (long) image->colors; i++)
1138 {
1139 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1140 image->colormap[i].green=image->colormap[i].red;
1141 image->colormap[i].blue=image->colormap[i].red;
1142 image->colormap[i].opacity=OpaqueOpacity;
1143 }
1144 else
1145 for (i=0; i < (long) image->colors; i++)
1146 {
1147 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1148 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1149 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
1150 image->colormap[i].opacity=OpaqueOpacity;
1151 }
1152 }
1153 scanline[0]=(JSAMPROW) jpeg_pixels;
1154 for (y=0; y < (long) image->rows; y++)
1155 {
1156 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001157 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001158
1159 register long
1160 x;
1161
1162 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001163 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001164
1165 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1166 {
1167 (void) ThrowMagickException(exception,GetMagickModule(),
1168 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1169 continue;
1170 }
1171 p=jpeg_pixels;
1172 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1173 if (q == (PixelPacket *) NULL)
1174 break;
1175 indexes=GetAuthenticIndexQueue(image);
1176 if (jpeg_info.data_precision > 8)
1177 {
1178 if (jpeg_info.output_components == 1)
1179 for (x=0; x < (long) image->columns; x++)
1180 {
1181 unsigned long
1182 pixel;
1183
1184 if (precision != 16)
1185 pixel=(unsigned long) GETJSAMPLE(*p);
1186 else
1187 pixel=(unsigned long) ((GETJSAMPLE(*p) ^ 0x80) << 4);
1188 jindex=ConstrainColormapIndex(image,pixel);
1189 indexes[x]=jindex;
1190 *q++=image->colormap[(int) jindex];
1191 p++;
1192 }
1193 else
1194 if (image->colorspace != CMYKColorspace)
1195 for (x=0; x < (long) image->columns; x++)
1196 {
1197 q->red=ScaleShortToQuantum((unsigned char)
1198 (GETJSAMPLE(*p++) << 4));
1199 q->green=ScaleShortToQuantum((unsigned char)
1200 (GETJSAMPLE(*p++) << 4));
1201 q->blue=ScaleShortToQuantum((unsigned char)
1202 (GETJSAMPLE(*p++) << 4));
1203 q->opacity=OpaqueOpacity;
1204 q++;
1205 }
1206 else
1207 for (x=0; x < (long) image->columns; x++)
1208 {
1209 q->red=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1210 (GETJSAMPLE(*p++) << 4));
1211 q->green=(Quantum) QuantumRange-ScaleShortToQuantum(
1212 (unsigned char) (GETJSAMPLE(*p++) << 4));
1213 q->blue=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1214 (GETJSAMPLE(*p++) << 4));
1215 q->opacity=OpaqueOpacity;
1216 indexes[x]=(IndexPacket) QuantumRange-ScaleShortToQuantum(
1217 (unsigned char) (GETJSAMPLE(*p++) << 4));
1218 q++;
1219 }
1220 }
1221 else
1222 if (jpeg_info.output_components == 1)
1223 for (x=0; x < (long) image->columns; x++)
1224 {
1225 jindex=ConstrainColormapIndex(image,(unsigned long) GETJSAMPLE(*p));
1226 indexes[x]=(IndexPacket) jindex;
1227 *q++=image->colormap[(int) jindex];
1228 p++;
1229 }
1230 else
1231 if (image->colorspace != CMYKColorspace)
1232 for (x=0; x < (long) image->columns; x++)
1233 {
1234 q->red=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1235 q->green=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1236 q->blue=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1237 q->opacity=OpaqueOpacity;
1238 q++;
1239 }
1240 else
1241 for (x=0; x < (long) image->columns; x++)
1242 {
1243 q->red=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1244 GETJSAMPLE(*p++));
1245 q->green=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1246 GETJSAMPLE(*p++));
1247 q->blue=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1248 GETJSAMPLE(*p++));
1249 q->opacity=OpaqueOpacity;
1250 indexes[x]=(IndexPacket) QuantumRange-ScaleCharToQuantum(
1251 (unsigned char) GETJSAMPLE(*p++));
1252 q++;
1253 }
1254 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1255 break;
1256 if (SetImageProgress(image,LoadImageTag,y,image->rows) == MagickFalse)
1257 break;
1258 }
1259 /*
1260 Free jpeg resources.
1261 */
1262 (void) jpeg_finish_decompress(&jpeg_info);
1263 jpeg_destroy_decompress(&jpeg_info);
1264 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1265 (void) CloseBlob(image);
1266 return(GetFirstImageInList(image));
1267}
1268#endif
1269
1270/*
1271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1272% %
1273% %
1274% %
1275% R e g i s t e r J P E G I m a g e %
1276% %
1277% %
1278% %
1279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1280%
1281% RegisterJPEGImage() adds properties for the JPEG image format to
1282% the list of supported formats. The properties include the image format
1283% tag, a method to read and/or write the format, whether the format
1284% supports the saving of more than one frame to the same file or blob,
1285% whether the format supports native in-memory I/O, and a brief
1286% description of the format.
1287%
1288% The format of the RegisterJPEGImage method is:
1289%
1290% unsigned long RegisterJPEGImage(void)
1291%
1292*/
1293ModuleExport unsigned long RegisterJPEGImage(void)
1294{
1295 char
1296 version[MaxTextExtent];
1297
1298 MagickInfo
1299 *entry;
1300
1301 static const char
cristy7138c592009-09-08 13:58:52 +00001302 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001303
1304 *version='\0';
1305#if defined(JPEG_LIB_VERSION)
1306 (void) FormatMagickString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
1307#endif
1308 entry=SetMagickInfo("JPEG");
1309 entry->thread_support=NoThreadSupport;
1310#if defined(MAGICKCORE_JPEG_DELEGATE)
1311 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1312 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1313#endif
1314 entry->magick=(IsImageFormatHandler *) IsJPEG;
1315 entry->adjoin=MagickFalse;
1316 entry->description=ConstantString(description);
1317 if (*version != '\0')
1318 entry->version=ConstantString(version);
1319 entry->module=ConstantString("JPEG");
1320 (void) RegisterMagickInfo(entry);
1321 entry=SetMagickInfo("JPG");
1322 entry->thread_support=NoThreadSupport;
1323#if defined(MAGICKCORE_JPEG_DELEGATE)
1324 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1325 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1326#endif
1327 entry->adjoin=MagickFalse;
1328 entry->description=ConstantString(description);
1329 if (*version != '\0')
1330 entry->version=ConstantString(version);
1331 entry->module=ConstantString("JPEG");
1332 (void) RegisterMagickInfo(entry);
1333 entry=SetMagickInfo("PJPEG");
1334 entry->thread_support=NoThreadSupport;
1335#if defined(MAGICKCORE_JPEG_DELEGATE)
1336 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1337 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1338#endif
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 return(MagickImageCoderSignature);
1346}
1347
1348/*
1349%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1350% %
1351% %
1352% %
1353% U n r e g i s t e r J P E G I m a g e %
1354% %
1355% %
1356% %
1357%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1358%
1359% UnregisterJPEGImage() removes format registrations made by the
1360% JPEG module from the list of supported formats.
1361%
1362% The format of the UnregisterJPEGImage method is:
1363%
1364% UnregisterJPEGImage(void)
1365%
1366*/
1367ModuleExport void UnregisterJPEGImage(void)
1368{
1369 (void) UnregisterMagickInfo("PJPG");
1370 (void) UnregisterMagickInfo("JPEG");
1371 (void) UnregisterMagickInfo("JPG");
1372}
1373
1374#if defined(MAGICKCORE_JPEG_DELEGATE)
1375/*
1376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1377% %
1378% %
1379% %
1380% W r i t e J P E G I m a g e %
1381% %
1382% %
1383% %
1384%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1385%
1386% WriteJPEGImage() writes a JPEG image file and returns it. It
1387% allocates the memory necessary for the new Image structure and returns a
1388% pointer to the new image.
1389%
1390% The format of the WriteJPEGImage method is:
1391%
1392% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,Image *image)
1393%
1394% A description of each parameter follows:
1395%
1396% o image_info: the image info.
1397%
1398% o jpeg_image: The image.
1399%
1400%
1401*/
1402
1403static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1404{
1405 DestinationManager
1406 *destination;
1407
1408 destination=(DestinationManager *) cinfo->dest;
1409 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1410 MaxBufferExtent,destination->buffer);
1411 if (destination->manager.free_in_buffer != MaxBufferExtent)
1412 ERREXIT(cinfo,JERR_FILE_WRITE);
1413 destination->manager.next_output_byte=destination->buffer;
1414 return(TRUE);
1415}
1416
1417static void InitializeDestination(j_compress_ptr cinfo)
1418{
1419 DestinationManager
1420 *destination;
1421
1422 destination=(DestinationManager *) cinfo->dest;
1423 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1424 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1425 destination->manager.next_output_byte=destination->buffer;
1426 destination->manager.free_in_buffer=MaxBufferExtent;
1427}
1428
1429static inline size_t MagickMin(const size_t x,const size_t y)
1430{
1431 if (x < y)
1432 return(x);
1433 return(y);
1434}
1435
1436static void TerminateDestination(j_compress_ptr cinfo)
1437{
1438 DestinationManager
1439 *destination;
1440
1441 destination=(DestinationManager *) cinfo->dest;
1442 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1443 {
1444 ssize_t
1445 count;
1446
1447 count=WriteBlob(destination->image,MaxBufferExtent-
1448 destination->manager.free_in_buffer,destination->buffer);
1449 if (count != (ssize_t)
1450 (MaxBufferExtent-destination->manager.free_in_buffer))
1451 ERREXIT(cinfo,JERR_FILE_WRITE);
1452 }
1453}
1454
1455static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1456{
1457 const char
1458 *name;
1459
1460 const StringInfo
1461 *profile;
1462
1463 MagickBooleanType
1464 iptc;
1465
1466 register long
1467 i;
1468
1469 size_t
1470 length;
1471
1472 StringInfo
1473 *custom_profile;
1474
1475 unsigned long
1476 tag_length;
1477
1478 /*
1479 Save image profile as a APP marker.
1480 */
1481 iptc=MagickFalse;
1482 custom_profile=AcquireStringInfo(65535L);
1483 ResetImageProfileIterator(image);
1484 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1485 {
1486 profile=GetImageProfile(image,name);
1487 if (LocaleCompare(name,"EXIF") == 0)
1488 for (i=0; i < (long) GetStringInfoLength(profile); i+=65533L)
1489 {
1490 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1491 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1492 (unsigned int) length);
1493 }
1494 if (LocaleCompare(name,"ICC") == 0)
1495 {
1496 register unsigned char
1497 *p;
1498
1499 tag_length=14;
1500 p=GetStringInfoDatum(custom_profile);
1501 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
1502 for (i=0; i < (long) GetStringInfoLength(profile); i+=65519L)
1503 {
1504 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
1505 p=GetStringInfoDatum(custom_profile);
1506 p[12]=(unsigned char) ((i/65519L)+1);
1507 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1508 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1509 length);
1510 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1511 custom_profile),(unsigned int) (length+tag_length));
1512 }
1513 }
1514 if (((LocaleCompare(name,"IPTC") == 0) ||
1515 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1516 {
1517 register unsigned char
1518 *p;
1519
1520 unsigned long
1521 roundup;
1522
1523 iptc=MagickTrue;
1524 p=GetStringInfoDatum(custom_profile);
1525 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1526 {
1527 (void) CopyMagickMemory(p,"Photoshop 3.0\0",14);
1528 tag_length=14;
1529 }
1530 else
1531 {
1532 (void) CopyMagickMemory(p,"Photoshop 3.0\08BIM\04\04\0\0\0\0",24);
1533 p[13]=0x00;
1534 p[24]=(unsigned char) (GetStringInfoLength(profile) >> 8);
1535 p[25]=(unsigned char) (GetStringInfoLength(profile) & 0xff);
1536 tag_length=26;
1537 }
1538 for (i=0; i < (long) GetStringInfoLength(profile); i+=65500L)
1539 {
1540 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
1541 roundup=(unsigned long) (length & 0x01);
1542 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1543 length);
1544 if (roundup != 0)
1545 p[length+tag_length]='\0';
1546 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1547 custom_profile),(unsigned int) (length+tag_length+roundup));
1548 }
1549 }
1550 if (LocaleCompare(name,"XMP") == 0)
1551 {
1552 StringInfo
1553 *xmp_profile;
1554
1555 /*
1556 Add namespace to XMP profile.
1557 */
1558 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/");
1559 ConcatenateStringInfo(xmp_profile,profile);
1560 GetStringInfoDatum(xmp_profile)[28]='\0';
1561 for (i=0; i < (long) GetStringInfoLength(xmp_profile); i+=65533L)
1562 {
1563 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1564 jpeg_write_marker(jpeg_info,XML_MARKER,
1565 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1566 }
1567 xmp_profile=DestroyStringInfo(xmp_profile);
1568 }
1569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s profile: %lu bytes",
1570 name,(unsigned long) GetStringInfoLength(profile));
1571 name=GetNextImageProfile(image);
1572 }
1573 custom_profile=DestroyStringInfo(custom_profile);
1574}
1575
1576static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1577{
1578 DestinationManager
1579 *destination;
1580
1581 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1582 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1583 destination=(DestinationManager *) cinfo->dest;
1584 destination->manager.init_destination=InitializeDestination;
1585 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1586 destination->manager.term_destination=TerminateDestination;
1587 destination->image=image;
1588}
1589
1590static char **SamplingFactorToList(const char *text)
1591{
1592 char
1593 **textlist;
1594
1595 register char
1596 *q;
1597
1598 register const char
1599 *p;
1600
1601 register long
1602 i;
1603
1604 unsigned long
1605 lines;
1606
1607 if (text == (char *) NULL)
1608 return((char **) NULL);
1609 /*
1610 Convert string to an ASCII list.
1611 */
1612 lines=1;
1613 for (p=text; *p != '\0'; p++)
1614 if (*p == ',')
1615 lines++;
1616 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1617 sizeof(*textlist));
1618 if (textlist == (char **) NULL)
1619 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1620 p=text;
1621 for (i=0; i < (long) lines; i++)
1622 {
1623 for (q=(char *) p; *q != '\0'; q++)
1624 if (*q == ',')
1625 break;
1626 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1627 sizeof(*textlist[i]));
1628 if (textlist[i] == (char *) NULL)
1629 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1630 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1631 if (*q == '\r')
1632 q++;
1633 p=q+1;
1634 }
1635 textlist[i]=(char *) NULL;
1636 return(textlist);
1637}
1638
1639static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1640 Image *image)
1641{
1642 const char
1643 *option,
1644 *sampling_factor,
1645 *value;
1646
1647 ErrorManager
1648 error_manager;
1649
1650 JSAMPLE
1651 *jpeg_pixels;
1652
1653 JSAMPROW
1654 scanline[1];
1655
1656 long
1657 y;
1658
1659 MagickBooleanType
1660 status;
1661
1662 register JSAMPLE
1663 *q;
1664
1665 register long
1666 i;
1667
1668 struct jpeg_compress_struct
1669 jpeg_info;
1670
1671 struct jpeg_error_mgr
1672 jpeg_error;
1673
1674 /*
1675 Open image file.
1676 */
1677 assert(image_info != (const ImageInfo *) NULL);
1678 assert(image_info->signature == MagickSignature);
1679 assert(image != (Image *) NULL);
1680 assert(image->signature == MagickSignature);
1681 if (image->debug != MagickFalse)
1682 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1683 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1684 if (status == MagickFalse)
1685 return(status);
1686 /*
1687 Initialize JPEG parameters.
1688 */
1689 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1690 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1691 jpeg_info.client_data=(void *) image;
1692 jpeg_info.err=jpeg_std_error(&jpeg_error);
1693 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
1694 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
1695 error_manager.image=image;
1696 jpeg_pixels=(JSAMPLE *) NULL;
1697 if (setjmp(error_manager.error_recovery) != 0)
1698 {
1699 jpeg_destroy_compress(&jpeg_info);
1700 (void) CloseBlob(image);
1701 return(MagickFalse);
1702 }
1703 jpeg_info.client_data=(void *) &error_manager;
1704 jpeg_create_compress(&jpeg_info);
1705 JPEGDestinationManager(&jpeg_info,image);
1706 if ((image->columns != (unsigned int) image->columns) ||
1707 (image->rows != (unsigned int) image->rows))
1708 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1709 jpeg_info.image_width=(unsigned int) image->columns;
1710 jpeg_info.image_height=(unsigned int) image->rows;
1711 jpeg_info.input_components=3;
1712 jpeg_info.data_precision=8;
1713 jpeg_info.in_color_space=JCS_RGB;
1714 switch (image->colorspace)
1715 {
1716 case CMYKColorspace:
1717 {
1718 jpeg_info.input_components=4;
1719 jpeg_info.in_color_space=JCS_CMYK;
1720 break;
1721 }
1722 case YCbCrColorspace:
1723 case Rec601YCbCrColorspace:
1724 case Rec709YCbCrColorspace:
1725 {
1726 jpeg_info.in_color_space=JCS_YCbCr;
1727 break;
1728 }
1729 case GRAYColorspace:
1730 case Rec601LumaColorspace:
1731 case Rec709LumaColorspace:
1732 {
1733 jpeg_info.input_components=1;
1734 jpeg_info.in_color_space=JCS_GRAYSCALE;
1735 break;
1736 }
1737 default:
1738 break;
1739 }
1740 if ((image_info->type != TrueColorType) &&
1741 (IsGrayImage(image,&image->exception) != MagickFalse))
1742 {
1743 jpeg_info.input_components=1;
1744 jpeg_info.in_color_space=JCS_GRAYSCALE;
1745 }
1746 jpeg_set_defaults(&jpeg_info);
1747 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1748 jpeg_info.data_precision=8;
1749 else
1750 if (sizeof(JSAMPLE) > 1)
1751 jpeg_info.data_precision=12;
1752 jpeg_info.density_unit=(UINT8) 1;
1753 if (image->debug != MagickFalse)
1754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1755 "Image resolution: %ld,%ld",(long) (image->x_resolution+0.5),
1756 (long) (image->y_resolution+0.5));
1757 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1758 {
1759 /*
1760 Set image resolution.
1761 */
1762 jpeg_info.write_JFIF_header=MagickTrue;
1763 jpeg_info.X_density=(UINT16) image->x_resolution;
1764 jpeg_info.Y_density=(UINT16) image->y_resolution;
1765 if (image->units == PixelsPerInchResolution)
1766 jpeg_info.density_unit=(UINT8) 1;
1767 if (image->units == PixelsPerCentimeterResolution)
1768 jpeg_info.density_unit=(UINT8) 2;
1769 }
1770 option=GetImageOption(image_info,"jpeg:dct-method");
1771 if (option != (const char *) NULL)
1772 switch (*option)
1773 {
1774 case 'D':
1775 case 'd':
1776 {
1777 if (LocaleCompare(option,"default") == 0)
1778 jpeg_info.dct_method=JDCT_DEFAULT;
1779 break;
1780 }
1781 case 'F':
1782 case 'f':
1783 {
1784 if (LocaleCompare(option,"fastest") == 0)
1785 jpeg_info.dct_method=JDCT_FASTEST;
1786 if (LocaleCompare(option,"float") == 0)
1787 jpeg_info.dct_method=JDCT_FLOAT;
1788 break;
1789 }
1790 case 'I':
1791 case 'i':
1792 {
1793 if (LocaleCompare(option,"ifast") == 0)
1794 jpeg_info.dct_method=JDCT_IFAST;
1795 if (LocaleCompare(option,"islow") == 0)
1796 jpeg_info.dct_method=JDCT_ISLOW;
1797 break;
1798 }
1799 }
1800 option=GetImageOption(image_info,"jpeg:optimize-coding");
1801 if (option != (const char *) NULL)
1802 {
1803 jpeg_info.optimize_coding=MagickFalse;
1804 if (IsMagickTrue(option) != MagickFalse)
1805 jpeg_info.optimize_coding=MagickTrue;
1806 }
1807 else
1808 {
1809 MagickSizeType
1810 length;
1811
1812 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1813 image->rows*sizeof(JSAMPLE);
1814 if (length == (MagickSizeType) ((size_t) length))
1815 {
1816 /*
1817 Perform optimization only if available memory resources permit it.
1818 */
1819 status=AcquireMagickResource(MemoryResource,length);
1820 if (status != MagickFalse)
1821 jpeg_info.optimize_coding=MagickTrue;
1822 RelinquishMagickResource(MemoryResource,length);
1823 }
1824 }
1825#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1826 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1827 (image_info->interlace != NoInterlace))
1828 {
1829 if (image->debug != MagickFalse)
1830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1831 "Interlace: progressive");
1832 jpeg_simple_progression(&jpeg_info);
1833 }
1834 else
1835 if (image->debug != MagickFalse)
1836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1837 "Interlace: non-progressive");
1838#else
1839 if (image->debug != MagickFalse)
1840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1841 "Interlace: nonprogressive");
1842#endif
cristy0adb4f92009-11-28 18:08:51 +00001843 option=GetImageOption(image_info,"jpeg:extent");
1844 if (option != (const char *) NULL)
1845 {
1846 Image
1847 *jpeg_image;
1848
1849 ImageInfo
1850 *jpeg_info;
1851
1852 jpeg_info=CloneImageInfo(image_info);
1853 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1854 if (jpeg_image != (Image *) NULL)
1855 {
1856 MagickSizeType
1857 extent;
1858
1859 size_t
1860 max,
1861 min;
1862
1863 /*
1864 Search for compression quality that does not exceed image extent.
1865 */
1866 jpeg_info->quality=0;
1867 extent=(MagickSizeType) StringToDouble(option,100.0);
1868 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1869 (void) AcquireUniqueFilename(jpeg_image->filename);
1870 min=0;
1871 for (max=100; (max-min) > 1; )
1872 {
1873 jpeg_image->quality=min+(max-min)/2;
1874 status=WriteJPEGImage(jpeg_info,jpeg_image);
1875 if (GetBlobSize(jpeg_image) < extent)
1876 min=jpeg_image->quality+1;
1877 else
1878 max=jpeg_image->quality-1;
1879 }
1880 (void) RelinquishUniqueFileResource(jpeg_image->filename);
1881 image->quality=max;
1882 jpeg_image=DestroyImage(jpeg_image);
1883 }
1884 jpeg_info=DestroyImageInfo(jpeg_info);
1885 }
cristy3ed852e2009-09-05 21:47:34 +00001886 if ((image_info->compression != LosslessJPEGCompression) &&
1887 (image->quality <= 100))
1888 {
1889 if (image->quality == UndefinedCompressionQuality)
1890 jpeg_set_quality(&jpeg_info,92,MagickTrue);
1891 else
1892 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
1893 if (image->debug != MagickFalse)
1894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %lu",
1895 image->quality);
1896 }
1897 else
1898 {
1899#if !defined(C_LOSSLESS_SUPPORTED)
1900 jpeg_set_quality(&jpeg_info,100,MagickTrue);
1901 if (image->debug != MagickFalse)
1902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
1903#else
1904 if (image->quality < 100)
1905 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1906 CoderWarning,"LosslessToLossyJPEGConversion",image->filename);
1907 else
1908 {
1909 int
1910 point_transform,
1911 predictor;
1912
1913 predictor=image->quality/100; /* range 1-7 */
1914 point_transform=image->quality % 20; /* range 0-15 */
1915 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
1916 if (image->debug != MagickFalse)
1917 {
1918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1919 "Compression: lossless");
1920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1921 "Predictor: %d",predictor);
1922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1923 "Point Transform: %d",point_transform);
1924 }
1925 }
1926#endif
1927 }
1928 sampling_factor=(const char *) NULL;
1929 value=GetImageProperty(image,"jpeg:sampling-factor");
1930 if (value != (char *) NULL)
1931 {
1932 sampling_factor=value;
1933 if (image->debug != MagickFalse)
1934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1935 " Input sampling-factors=%s",sampling_factor);
1936 }
1937 if (image_info->sampling_factor != (char *) NULL)
1938 sampling_factor=image_info->sampling_factor;
1939 if (sampling_factor == (const char *) NULL)
1940 {
1941 if (image->quality >= 90)
1942 for (i=0; i < MAX_COMPONENTS; i++)
1943 {
1944 jpeg_info.comp_info[i].h_samp_factor=1;
1945 jpeg_info.comp_info[i].v_samp_factor=1;
1946 }
1947 }
1948 else
1949 {
1950 char
1951 **factors;
1952
1953 GeometryInfo
1954 geometry_info;
1955
1956 MagickStatusType
1957 flags;
1958
1959 /*
1960 Set sampling factor.
1961 */
1962 i=0;
1963 factors=SamplingFactorToList(sampling_factor);
1964 if (factors != (char **) NULL)
1965 {
1966 for (i=0; i < MAX_COMPONENTS; i++)
1967 {
1968 if (factors[i] == (char *) NULL)
1969 break;
1970 flags=ParseGeometry(factors[i],&geometry_info);
1971 if ((flags & SigmaValue) == 0)
1972 geometry_info.sigma=geometry_info.rho;
1973 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
1974 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
1975 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
1976 }
1977 factors=(char **) RelinquishMagickMemory(factors);
1978 }
1979 for ( ; i < MAX_COMPONENTS; i++)
1980 {
1981 jpeg_info.comp_info[i].h_samp_factor=1;
1982 jpeg_info.comp_info[i].v_samp_factor=1;
1983 }
1984 }
1985 if (jpeg_info.input_components == 1)
1986 for (i=0; i < MAX_COMPONENTS; i++)
1987 {
1988 jpeg_info.comp_info[i].h_samp_factor=1;
1989 jpeg_info.comp_info[i].v_samp_factor=1;
1990 }
1991 jpeg_start_compress(&jpeg_info,MagickTrue);
1992 if (image->debug != MagickFalse)
1993 {
1994 if (image->storage_class == PseudoClass)
1995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1996 "Storage class: PseudoClass");
1997 else
1998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1999 "Storage class: DirectClass");
2000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %lu",
2001 image->depth);
2002 if (image->colors != 0)
2003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2004 "Number of colors: %lu",image->colors);
2005 else
2006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2007 "Number of colors: unspecified");
2008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2009 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2010 switch (image->colorspace)
2011 {
2012 case CMYKColorspace:
2013 {
2014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2015 "Storage class: DirectClass");
2016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2017 "Colorspace: CMYK");
2018 break;
2019 }
2020 case YCbCrColorspace:
2021 case Rec601YCbCrColorspace:
2022 case Rec709YCbCrColorspace:
2023 {
2024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2025 "Colorspace: YCbCr");
2026 break;
2027 }
2028 default:
2029 break;
2030 }
2031 switch (image->colorspace)
2032 {
2033 case CMYKColorspace:
2034 {
2035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2036 "Colorspace: CMYK");
2037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2038 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2039 jpeg_info.comp_info[0].h_samp_factor,
2040 jpeg_info.comp_info[0].v_samp_factor,
2041 jpeg_info.comp_info[1].h_samp_factor,
2042 jpeg_info.comp_info[1].v_samp_factor,
2043 jpeg_info.comp_info[2].h_samp_factor,
2044 jpeg_info.comp_info[2].v_samp_factor,
2045 jpeg_info.comp_info[3].h_samp_factor,
2046 jpeg_info.comp_info[3].v_samp_factor);
2047 break;
2048 }
2049 case GRAYColorspace:
2050 case Rec601LumaColorspace:
2051 case Rec709LumaColorspace:
2052 {
2053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2054 "Colorspace: GRAY");
2055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2056 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2057 jpeg_info.comp_info[0].v_samp_factor);
2058 break;
2059 }
2060 case RGBColorspace:
2061 {
2062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2063 "Image colorspace is RGB");
2064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2065 "Sampling factors: %dx%d,%dx%d,%dx%d",
2066 jpeg_info.comp_info[0].h_samp_factor,
2067 jpeg_info.comp_info[0].v_samp_factor,
2068 jpeg_info.comp_info[1].h_samp_factor,
2069 jpeg_info.comp_info[1].v_samp_factor,
2070 jpeg_info.comp_info[2].h_samp_factor,
2071 jpeg_info.comp_info[2].v_samp_factor);
2072 break;
2073 }
2074 case YCbCrColorspace:
2075 case Rec601YCbCrColorspace:
2076 case Rec709YCbCrColorspace:
2077 {
2078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2079 "Colorspace: YCbCr");
2080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2081 "Sampling factors: %dx%d,%dx%d,%dx%d",
2082 jpeg_info.comp_info[0].h_samp_factor,
2083 jpeg_info.comp_info[0].v_samp_factor,
2084 jpeg_info.comp_info[1].h_samp_factor,
2085 jpeg_info.comp_info[1].v_samp_factor,
2086 jpeg_info.comp_info[2].h_samp_factor,
2087 jpeg_info.comp_info[2].v_samp_factor);
2088 break;
2089 }
2090 default:
2091 {
2092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2093 image->colorspace);
2094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2095 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2096 jpeg_info.comp_info[0].h_samp_factor,
2097 jpeg_info.comp_info[0].v_samp_factor,
2098 jpeg_info.comp_info[1].h_samp_factor,
2099 jpeg_info.comp_info[1].v_samp_factor,
2100 jpeg_info.comp_info[2].h_samp_factor,
2101 jpeg_info.comp_info[2].v_samp_factor,
2102 jpeg_info.comp_info[3].h_samp_factor,
2103 jpeg_info.comp_info[3].v_samp_factor);
2104 break;
2105 }
2106 }
2107 }
2108 /*
2109 Write JPEG profiles.
2110 */
2111 value=GetImageProperty(image,"comment");
2112 if (value != (char *) NULL)
2113 for (i=0; i < (long) strlen(value); i+=65533L)
2114 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2115 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2116 if (image->profiles != (void *) NULL)
2117 WriteProfile(&jpeg_info,image);
2118 /*
2119 Convert MIFF to JPEG raster pixels.
2120 */
2121 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2122 jpeg_info.input_components*sizeof(*jpeg_pixels));
2123 if (jpeg_pixels == (JSAMPLE *) NULL)
2124 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2125 if (setjmp(error_manager.error_recovery) != 0)
2126 {
2127 jpeg_destroy_compress(&jpeg_info);
2128 if (jpeg_pixels != (unsigned char *) NULL)
2129 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2130 (void) CloseBlob(image);
2131 return(MagickFalse);
2132 }
2133 scanline[0]=(JSAMPROW) jpeg_pixels;
2134 if (jpeg_info.data_precision > 8)
2135 {
2136 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2137 for (y=0; y < (long) image->rows; y++)
2138 {
2139 register const PixelPacket
2140 *p;
2141
2142 register long
2143 x;
2144
2145 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2146 if (p == (const PixelPacket *) NULL)
2147 break;
2148 q=jpeg_pixels;
2149 for (x=0; x < (long) image->columns; x++)
2150 {
2151 *q++=(JSAMPLE) (ScaleQuantumToShort(PixelIntensityToQuantum(p)) >>
2152 4);
2153 p++;
2154 }
2155 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2156 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2157 break;
2158 }
2159 else
2160 if ((jpeg_info.in_color_space == JCS_RGB) ||
2161 (jpeg_info.in_color_space == JCS_YCbCr))
2162 for (y=0; y < (long) image->rows; y++)
2163 {
2164 register const PixelPacket
2165 *p;
2166
2167 register long
2168 x;
2169
2170 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2171 if (p == (const PixelPacket *) NULL)
2172 break;
2173 q=jpeg_pixels;
2174 for (x=0; x < (long) image->columns; x++)
2175 {
2176 *q++=(JSAMPLE) (ScaleQuantumToShort(p->red) >> 4);
2177 *q++=(JSAMPLE) (ScaleQuantumToShort(p->green) >> 4);
2178 *q++=(JSAMPLE) (ScaleQuantumToShort(p->blue) >> 4);
2179 p++;
2180 }
2181 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2182 status=SetImageProgress(image,SaveImageTag,y,image->rows);
2183 if (status == MagickFalse)
2184 break;
2185 }
2186 else
2187 for (y=0; y < (long) image->rows; y++)
2188 {
2189 register const IndexPacket
2190 *indexes;
2191
2192 register const PixelPacket
2193 *p;
2194
2195 register long
2196 x;
2197
2198 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2199 if (p == (const PixelPacket *) NULL)
2200 break;
2201 q=jpeg_pixels;
2202 indexes=GetVirtualIndexQueue(image);
2203 for (x=0; x < (long) image->columns; x++)
2204 {
2205 /*
2206 Convert DirectClass packets to contiguous CMYK scanlines.
2207 */
2208 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(p->red) >> 4));
2209 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(p->green) >> 4));
2210 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(p->blue) >> 4));
2211 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(indexes[x]) >> 4));
2212 p++;
2213 }
2214 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2215 status=SetImageProgress(image,SaveImageTag,y,image->rows);
2216 if (status == MagickFalse)
2217 break;
2218 }
2219 }
2220 else
2221 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2222 for (y=0; y < (long) image->rows; y++)
2223 {
2224 register const PixelPacket
2225 *p;
2226
2227 register long
2228 x;
2229
2230 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2231 if (p == (const PixelPacket *) NULL)
2232 break;
2233 q=jpeg_pixels;
2234 for (x=0; x < (long) image->columns; x++)
2235 {
2236 *q++=(JSAMPLE) ScaleQuantumToChar(PixelIntensityToQuantum(p));
2237 p++;
2238 }
2239 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2240 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2241 break;
2242 }
2243 else
2244 if ((jpeg_info.in_color_space == JCS_RGB) ||
2245 (jpeg_info.in_color_space == JCS_YCbCr))
2246 for (y=0; y < (long) image->rows; y++)
2247 {
2248 register const PixelPacket
2249 *p;
2250
2251 register long
2252 x;
2253
2254 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2255 if (p == (const PixelPacket *) NULL)
2256 break;
2257 q=jpeg_pixels;
2258 for (x=0; x < (long) image->columns; x++)
2259 {
2260 *q++=(JSAMPLE) ScaleQuantumToChar(p->red);
2261 *q++=(JSAMPLE) ScaleQuantumToChar(p->green);
2262 *q++=(JSAMPLE) ScaleQuantumToChar(p->blue);
2263 p++;
2264 }
2265 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2266 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2267 break;
2268 }
2269 else
2270 for (y=0; y < (long) image->rows; y++)
2271 {
2272 register const IndexPacket
2273 *indexes;
2274
2275 register const PixelPacket
2276 *p;
2277
2278 register long
2279 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;
2285 indexes=GetVirtualIndexQueue(image);
2286 for (x=0; x < (long) image->columns; x++)
2287 {
2288 /*
2289 Convert DirectClass packets to contiguous CMYK scanlines.
2290 */
2291 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2292 p->red)));
2293 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2294 p->green)));
2295 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2296 p->blue)));
2297 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2298 indexes[x])));
2299 p++;
2300 }
2301 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2302 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2303 break;
2304 }
2305 if (y == (long) image->rows)
2306 jpeg_finish_compress(&jpeg_info);
2307 /*
2308 Relinquish resources.
2309 */
2310 jpeg_destroy_compress(&jpeg_info);
2311 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2312 (void) CloseBlob(image);
2313 return(MagickTrue);
2314}
2315#endif