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