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