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