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