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