blob: 460c33c3f6baef920a97626c71ed4c9fb5eee13d [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;
cristyb6c017e2010-01-13 19:11:39 +0000734 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristy3ed852e2009-09-05 21:47:34 +0000735 image->quality=(unsigned long) i+1;
736 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
738 "Quality: %ld (%s)",i+1,(qvalue <= hash[i]) &&
739 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000740 break;
741 }
742 }
743 else
744 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
745 {
746 long
747 hash[101] =
748 {
749 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
750 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
751 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
752 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
753 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
754 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
755 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
756 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
757 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
758 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
759 0
760 },
761 sums[101] =
762 {
763 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
764 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
765 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
766 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
767 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
768 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
769 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
770 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
771 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
772 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
773 667, 592, 518, 441, 369, 292, 221, 151, 86,
774 64, 0
775 };
776
777 qvalue=(long) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
778 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
779 for (i=0; i < 100; i++)
780 {
781 if ((qvalue < hash[i]) && (sum < sums[i]))
782 continue;
cristyb6c017e2010-01-13 19:11:39 +0000783 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
784 image->quality=(unsigned long) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000785 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
787 "Quality: %ld (%s)",i+1,(qvalue <= hash[i]) &&
788 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000789 break;
790 }
791 }
792 }
793}
794
795static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image)
796{
797 char
798 sampling_factor[MaxTextExtent];
799
800 switch (jpeg_info->out_color_space)
801 {
802 case JCS_CMYK:
803 {
804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
805 (void) FormatMagickString(sampling_factor,MaxTextExtent,
806 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
807 jpeg_info->comp_info[0].v_samp_factor,
808 jpeg_info->comp_info[1].h_samp_factor,
809 jpeg_info->comp_info[1].v_samp_factor,
810 jpeg_info->comp_info[2].h_samp_factor,
811 jpeg_info->comp_info[2].v_samp_factor,
812 jpeg_info->comp_info[3].h_samp_factor,
813 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000814 break;
cristy3ed852e2009-09-05 21:47:34 +0000815 }
816 case JCS_GRAYSCALE:
817 {
818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
819 "Colorspace: GRAYSCALE");
820 (void) FormatMagickString(sampling_factor,MaxTextExtent,"%dx%d",
821 jpeg_info->comp_info[0].h_samp_factor,
822 jpeg_info->comp_info[0].v_samp_factor);
823 break;
824 }
825 case JCS_RGB:
826 {
827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
828 (void) FormatMagickString(sampling_factor,MaxTextExtent,
829 "%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 break;
836 }
837 default:
838 {
839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
840 jpeg_info->out_color_space);
841 (void) FormatMagickString(sampling_factor,MaxTextExtent,
842 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
843 jpeg_info->comp_info[0].v_samp_factor,
844 jpeg_info->comp_info[1].h_samp_factor,
845 jpeg_info->comp_info[1].v_samp_factor,
846 jpeg_info->comp_info[2].h_samp_factor,
847 jpeg_info->comp_info[2].v_samp_factor,
848 jpeg_info->comp_info[3].h_samp_factor,
849 jpeg_info->comp_info[3].v_samp_factor);
850 break;
851 }
852 }
853 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor);
cristye90d7402010-03-14 18:21:29 +0000854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
855 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000856}
857
858static Image *ReadJPEGImage(const ImageInfo *image_info,
859 ExceptionInfo *exception)
860{
861 char
862 value[MaxTextExtent];
863
cristycaec74e2009-09-14 02:20:04 +0000864 const char
cristy11151212009-09-14 13:10:15 +0000865 *option;
cristycaec74e2009-09-14 02:20:04 +0000866
cristy3ed852e2009-09-05 21:47:34 +0000867 ErrorManager
868 error_manager;
869
cristy3ed852e2009-09-05 21:47:34 +0000870 Image
871 *image;
872
cristya0511732010-02-17 02:38:35 +0000873 IndexPacket
874 index;
875
cristy3ed852e2009-09-05 21:47:34 +0000876 long
877 y;
878
879 JSAMPLE
880 *jpeg_pixels;
881
882 JSAMPROW
883 scanline[1];
884
885 MagickBooleanType
886 debug,
887 status;
888
889 MagickSizeType
890 number_pixels;
891
892 register long
893 i;
894
895 struct jpeg_decompress_struct
896 jpeg_info;
897
898 struct jpeg_error_mgr
899 jpeg_error;
900
901 register JSAMPLE
902 *p;
903
904 unsigned long
905 precision,
906 units;
907
908 /*
909 Open image file.
910 */
911 assert(image_info != (const ImageInfo *) NULL);
912 assert(image_info->signature == MagickSignature);
913 if (image_info->debug != MagickFalse)
914 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
915 image_info->filename);
916 assert(exception != (ExceptionInfo *) NULL);
917 assert(exception->signature == MagickSignature);
918 debug=IsEventLogging();
919 image=AcquireImage(image_info);
920 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
921 if (status == MagickFalse)
922 {
923 image=DestroyImageList(image);
924 return((Image *) NULL);
925 }
926 /*
927 Initialize JPEG parameters.
928 */
929 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
930 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
931 jpeg_info.err=jpeg_std_error(&jpeg_error);
932 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
933 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
934 jpeg_pixels=(JSAMPLE *) NULL;
935 error_manager.image=image;
936 if (setjmp(error_manager.error_recovery) != 0)
937 {
938 jpeg_destroy_decompress(&jpeg_info);
939 (void) CloseBlob(image);
940 number_pixels=(MagickSizeType) image->columns*image->rows;
941 if (number_pixels != 0)
942 return(GetFirstImageInList(image));
cristy195f6d12009-11-05 18:16:03 +0000943 InheritException(exception,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +0000944 return(DestroyImage(image));
945 }
946 jpeg_info.client_data=(void *) &error_manager;
947 jpeg_create_decompress(&jpeg_info);
948 JPEGSourceManager(&jpeg_info,image);
949 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
950 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
951 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
952 for (i=1; i < 16; i++)
953 if ((i != 2) && (i != 13) && (i != 14))
954 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
955 i=jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +0000956 if ((image_info->colorspace == YCbCrColorspace) ||
957 (image_info->colorspace == Rec601YCbCrColorspace) ||
958 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +0000959 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +0000960 if (IsITUFaxImage(image) != MagickFalse)
961 {
962 image->colorspace=LabColorspace;
963 jpeg_info.out_color_space=JCS_YCbCr;
964 }
965 else
966 if (jpeg_info.out_color_space == JCS_CMYK)
967 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000968 /*
969 Set image resolution.
970 */
971 units=0;
972 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
973 (jpeg_info.Y_density != 1))
974 {
975 image->x_resolution=(double) jpeg_info.X_density;
976 image->y_resolution=(double) jpeg_info.Y_density;
977 units=(unsigned long) jpeg_info.density_unit;
978 }
979 if (units == 1)
980 image->units=PixelsPerInchResolution;
981 if (units == 2)
982 image->units=PixelsPerCentimeterResolution;
983 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +0000984 option=GetImageOption(image_info,"jpeg:size");
985 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000986 {
987 double
988 scale_factor;
989
cristycaec74e2009-09-14 02:20:04 +0000990 GeometryInfo
991 geometry_info;
992
cristy0adb4f92009-11-28 18:08:51 +0000993 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +0000994 flags;
995
cristy3ed852e2009-09-05 21:47:34 +0000996 /*
cristycaec74e2009-09-14 02:20:04 +0000997 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +0000998 */
cristy11151212009-09-14 13:10:15 +0000999 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001000 if ((flags & SigmaValue) == 0)
1001 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001002 jpeg_calc_output_dimensions(&jpeg_info);
1003 image->magick_columns=jpeg_info.output_width;
1004 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001005 scale_factor=1.0;
1006 if (geometry_info.rho != 0.0)
1007 scale_factor=jpeg_info.output_width/geometry_info.rho;
1008 if ((geometry_info.sigma != 0.0) &&
1009 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1010 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001011 jpeg_info.scale_num=1U;
1012 jpeg_info.scale_denom=(unsigned int) scale_factor;
1013 jpeg_calc_output_dimensions(&jpeg_info);
1014 if (image->debug != MagickFalse)
1015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Scale factor: %ld",
1016 (long) scale_factor);
1017 }
1018 precision=(unsigned long) jpeg_info.data_precision;
1019#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1020#if defined(D_LOSSLESS_SUPPORTED)
1021 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1022 JPEGInterlace : NoInterlace;
1023 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1024 LosslessJPEGCompression : JPEGCompression;
1025 if (jpeg_info.data_precision > 8)
1026 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1027 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1028 image->filename);
1029 if (jpeg_info.data_precision == 16)
1030 jpeg_info.data_precision=12;
1031#else
1032 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1033 NoInterlace;
1034 image->compression=JPEGCompression;
1035#endif
1036#else
1037 image->compression=JPEGCompression;
1038 image->interlace=JPEGInterlace;
1039#endif
1040 if ((image_info->colors > 8) && (image_info->colors <= 256))
1041 {
1042 /*
1043 Let the JPEG library quantize for us.
1044 */
1045 jpeg_info.quantize_colors=MagickTrue;
1046 jpeg_info.desired_number_of_colors=(int) image_info->colors;
1047 }
cristy1b8d4462009-10-27 13:46:56 +00001048 option=GetImageOption(image_info,"jpeg:block-smoothing");
1049 if (option != (const char *) NULL)
1050 {
1051 jpeg_info.do_block_smoothing=MagickFalse;
1052 if (IsMagickTrue(option) != MagickFalse)
1053 jpeg_info.do_block_smoothing=MagickTrue;
1054 }
cristy787d4352010-03-06 13:55:58 +00001055 option=GetImageOption(image_info,"jpeg:dct-method");
1056 if (option != (const char *) NULL)
1057 switch (*option)
1058 {
1059 case 'D':
1060 case 'd':
1061 {
1062 if (LocaleCompare(option,"default") == 0)
1063 jpeg_info.dct_method=JDCT_DEFAULT;
1064 break;
1065 }
1066 case 'F':
1067 case 'f':
1068 {
1069 if (LocaleCompare(option,"fastest") == 0)
1070 jpeg_info.dct_method=JDCT_FASTEST;
1071 if (LocaleCompare(option,"float") == 0)
1072 jpeg_info.dct_method=JDCT_FLOAT;
1073 break;
1074 }
1075 case 'I':
1076 case 'i':
1077 {
1078 if (LocaleCompare(option,"ifast") == 0)
1079 jpeg_info.dct_method=JDCT_IFAST;
1080 if (LocaleCompare(option,"islow") == 0)
1081 jpeg_info.dct_method=JDCT_ISLOW;
1082 break;
1083 }
1084 }
cristy1b8d4462009-10-27 13:46:56 +00001085 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1086 if (option != (const char *) NULL)
1087 {
1088 jpeg_info.do_fancy_upsampling=MagickFalse;
1089 if (IsMagickTrue(option) != MagickFalse)
1090 jpeg_info.do_fancy_upsampling=MagickTrue;
1091 }
cristy3ed852e2009-09-05 21:47:34 +00001092 (void) jpeg_start_decompress(&jpeg_info);
1093 image->columns=jpeg_info.output_width;
1094 image->rows=jpeg_info.output_height;
1095 image->depth=(unsigned long) jpeg_info.data_precision;
1096 if (jpeg_info.out_color_space == JCS_YCbCr)
1097 image->colorspace=YCbCrColorspace;
1098 if (jpeg_info.out_color_space == JCS_CMYK)
1099 image->colorspace=CMYKColorspace;
1100 if ((image_info->colors != 0) && (image_info->colors <= 256))
1101 if (AcquireImageColormap(image,image_info->colors) == MagickFalse)
1102 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1103 if ((jpeg_info.output_components == 1) &&
1104 (jpeg_info.quantize_colors == MagickFalse))
1105 {
1106 unsigned long
1107 colors;
1108
1109 colors=(unsigned long) GetQuantumRange(image->depth)+1;
1110 if (AcquireImageColormap(image,colors) == MagickFalse)
1111 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1112 }
1113 if (image->debug != MagickFalse)
1114 {
1115 if (image->interlace != NoInterlace)
1116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1117 "Interlace: progressive");
1118 else
1119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1120 "Interlace: nonprogressive");
1121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1122 (int) jpeg_info.data_precision);
1123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1124 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1125 }
1126 JPEGSetImageQuality(&jpeg_info,image);
1127 JPEGSetImageSamplingFactor(&jpeg_info,image);
1128 (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
1129 jpeg_info.out_color_space);
1130 (void) SetImageProperty(image,"jpeg:colorspace",value);
1131 if (image_info->ping != MagickFalse)
1132 {
1133 jpeg_destroy_decompress(&jpeg_info);
1134 (void) CloseBlob(image);
1135 return(GetFirstImageInList(image));
1136 }
1137 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1138 jpeg_info.output_components*sizeof(JSAMPLE));
1139 if (jpeg_pixels == (JSAMPLE *) NULL)
1140 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1141 /*
1142 Convert JPEG pixels to pixel packets.
1143 */
1144 if (setjmp(error_manager.error_recovery) != 0)
1145 {
1146 if (jpeg_pixels != (unsigned char *) NULL)
1147 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1148 jpeg_destroy_decompress(&jpeg_info);
1149 (void) CloseBlob(image);
1150 number_pixels=(MagickSizeType) image->columns*image->rows;
1151 if (number_pixels != 0)
1152 return(GetFirstImageInList(image));
1153 return(DestroyImage(image));
1154 }
1155 if (jpeg_info.quantize_colors != MagickFalse)
1156 {
1157 image->colors=(unsigned long) jpeg_info.actual_number_of_colors;
1158 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
1159 for (i=0; i < (long) image->colors; i++)
1160 {
1161 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1162 image->colormap[i].green=image->colormap[i].red;
1163 image->colormap[i].blue=image->colormap[i].red;
1164 image->colormap[i].opacity=OpaqueOpacity;
1165 }
1166 else
1167 for (i=0; i < (long) image->colors; i++)
1168 {
1169 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1170 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1171 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
1172 image->colormap[i].opacity=OpaqueOpacity;
1173 }
1174 }
1175 scanline[0]=(JSAMPROW) jpeg_pixels;
1176 for (y=0; y < (long) image->rows; y++)
1177 {
1178 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001179 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001180
1181 register long
1182 x;
1183
1184 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001185 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001186
1187 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1188 {
1189 (void) ThrowMagickException(exception,GetMagickModule(),
1190 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1191 continue;
1192 }
1193 p=jpeg_pixels;
1194 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1195 if (q == (PixelPacket *) NULL)
1196 break;
1197 indexes=GetAuthenticIndexQueue(image);
1198 if (jpeg_info.data_precision > 8)
1199 {
1200 if (jpeg_info.output_components == 1)
1201 for (x=0; x < (long) image->columns; x++)
1202 {
1203 unsigned long
1204 pixel;
1205
1206 if (precision != 16)
1207 pixel=(unsigned long) GETJSAMPLE(*p);
1208 else
1209 pixel=(unsigned long) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristya0511732010-02-17 02:38:35 +00001210 index=ConstrainColormapIndex(image,pixel);
1211 indexes[x]=index;
1212 *q++=image->colormap[(int) index];
cristy3ed852e2009-09-05 21:47:34 +00001213 p++;
1214 }
1215 else
1216 if (image->colorspace != CMYKColorspace)
1217 for (x=0; x < (long) image->columns; x++)
1218 {
1219 q->red=ScaleShortToQuantum((unsigned char)
1220 (GETJSAMPLE(*p++) << 4));
1221 q->green=ScaleShortToQuantum((unsigned char)
1222 (GETJSAMPLE(*p++) << 4));
1223 q->blue=ScaleShortToQuantum((unsigned char)
1224 (GETJSAMPLE(*p++) << 4));
cristyce70c172010-01-07 17:15:30 +00001225 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001226 q++;
1227 }
1228 else
1229 for (x=0; x < (long) image->columns; x++)
1230 {
1231 q->red=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1232 (GETJSAMPLE(*p++) << 4));
1233 q->green=(Quantum) QuantumRange-ScaleShortToQuantum(
1234 (unsigned char) (GETJSAMPLE(*p++) << 4));
1235 q->blue=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
1236 (GETJSAMPLE(*p++) << 4));
cristyce70c172010-01-07 17:15:30 +00001237 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001238 indexes[x]=(IndexPacket) QuantumRange-ScaleShortToQuantum(
1239 (unsigned char) (GETJSAMPLE(*p++) << 4));
1240 q++;
1241 }
1242 }
1243 else
1244 if (jpeg_info.output_components == 1)
1245 for (x=0; x < (long) image->columns; x++)
1246 {
cristya0511732010-02-17 02:38:35 +00001247 index=ConstrainColormapIndex(image,(unsigned long) GETJSAMPLE(*p));
1248 indexes[x]=(IndexPacket) index;
1249 *q++=image->colormap[(int) index];
cristy3ed852e2009-09-05 21:47:34 +00001250 p++;
1251 }
1252 else
1253 if (image->colorspace != CMYKColorspace)
1254 for (x=0; x < (long) image->columns; x++)
1255 {
1256 q->red=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1257 q->green=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
1258 q->blue=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
cristyce70c172010-01-07 17:15:30 +00001259 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001260 q++;
1261 }
1262 else
1263 for (x=0; x < (long) image->columns; x++)
1264 {
1265 q->red=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1266 GETJSAMPLE(*p++));
1267 q->green=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1268 GETJSAMPLE(*p++));
1269 q->blue=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
1270 GETJSAMPLE(*p++));
cristyce70c172010-01-07 17:15:30 +00001271 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001272 indexes[x]=(IndexPacket) QuantumRange-ScaleCharToQuantum(
1273 (unsigned char) GETJSAMPLE(*p++));
1274 q++;
1275 }
1276 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1277 break;
1278 if (SetImageProgress(image,LoadImageTag,y,image->rows) == MagickFalse)
1279 break;
1280 }
1281 /*
1282 Free jpeg resources.
1283 */
1284 (void) jpeg_finish_decompress(&jpeg_info);
1285 jpeg_destroy_decompress(&jpeg_info);
1286 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1287 (void) CloseBlob(image);
1288 return(GetFirstImageInList(image));
1289}
1290#endif
1291
1292/*
1293%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1294% %
1295% %
1296% %
1297% R e g i s t e r J P E G I m a g e %
1298% %
1299% %
1300% %
1301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1302%
1303% RegisterJPEGImage() adds properties for the JPEG image format to
1304% the list of supported formats. The properties include the image format
1305% tag, a method to read and/or write the format, whether the format
1306% supports the saving of more than one frame to the same file or blob,
1307% whether the format supports native in-memory I/O, and a brief
1308% description of the format.
1309%
1310% The format of the RegisterJPEGImage method is:
1311%
1312% unsigned long RegisterJPEGImage(void)
1313%
1314*/
1315ModuleExport unsigned long RegisterJPEGImage(void)
1316{
1317 char
1318 version[MaxTextExtent];
1319
1320 MagickInfo
1321 *entry;
1322
1323 static const char
cristy7138c592009-09-08 13:58:52 +00001324 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001325
1326 *version='\0';
1327#if defined(JPEG_LIB_VERSION)
1328 (void) FormatMagickString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
1329#endif
1330 entry=SetMagickInfo("JPEG");
1331 entry->thread_support=NoThreadSupport;
1332#if defined(MAGICKCORE_JPEG_DELEGATE)
1333 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1334 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1335#endif
1336 entry->magick=(IsImageFormatHandler *) IsJPEG;
1337 entry->adjoin=MagickFalse;
1338 entry->description=ConstantString(description);
1339 if (*version != '\0')
1340 entry->version=ConstantString(version);
1341 entry->module=ConstantString("JPEG");
1342 (void) RegisterMagickInfo(entry);
1343 entry=SetMagickInfo("JPG");
1344 entry->thread_support=NoThreadSupport;
1345#if defined(MAGICKCORE_JPEG_DELEGATE)
1346 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1347 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1348#endif
1349 entry->adjoin=MagickFalse;
1350 entry->description=ConstantString(description);
1351 if (*version != '\0')
1352 entry->version=ConstantString(version);
1353 entry->module=ConstantString("JPEG");
1354 (void) RegisterMagickInfo(entry);
1355 entry=SetMagickInfo("PJPEG");
1356 entry->thread_support=NoThreadSupport;
1357#if defined(MAGICKCORE_JPEG_DELEGATE)
1358 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1359 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1360#endif
1361 entry->adjoin=MagickFalse;
1362 entry->description=ConstantString(description);
1363 if (*version != '\0')
1364 entry->version=ConstantString(version);
1365 entry->module=ConstantString("JPEG");
1366 (void) RegisterMagickInfo(entry);
1367 return(MagickImageCoderSignature);
1368}
1369
1370/*
1371%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1372% %
1373% %
1374% %
1375% U n r e g i s t e r J P E G I m a g e %
1376% %
1377% %
1378% %
1379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1380%
1381% UnregisterJPEGImage() removes format registrations made by the
1382% JPEG module from the list of supported formats.
1383%
1384% The format of the UnregisterJPEGImage method is:
1385%
1386% UnregisterJPEGImage(void)
1387%
1388*/
1389ModuleExport void UnregisterJPEGImage(void)
1390{
1391 (void) UnregisterMagickInfo("PJPG");
1392 (void) UnregisterMagickInfo("JPEG");
1393 (void) UnregisterMagickInfo("JPG");
1394}
1395
1396#if defined(MAGICKCORE_JPEG_DELEGATE)
1397/*
1398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1399% %
1400% %
1401% %
1402% W r i t e J P E G I m a g e %
1403% %
1404% %
1405% %
1406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1407%
1408% WriteJPEGImage() writes a JPEG image file and returns it. It
1409% allocates the memory necessary for the new Image structure and returns a
1410% pointer to the new image.
1411%
1412% The format of the WriteJPEGImage method is:
1413%
1414% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,Image *image)
1415%
1416% A description of each parameter follows:
1417%
1418% o image_info: the image info.
1419%
1420% o jpeg_image: The image.
1421%
1422%
1423*/
1424
1425static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1426{
1427 DestinationManager
1428 *destination;
1429
1430 destination=(DestinationManager *) cinfo->dest;
1431 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1432 MaxBufferExtent,destination->buffer);
1433 if (destination->manager.free_in_buffer != MaxBufferExtent)
1434 ERREXIT(cinfo,JERR_FILE_WRITE);
1435 destination->manager.next_output_byte=destination->buffer;
1436 return(TRUE);
1437}
1438
1439static void InitializeDestination(j_compress_ptr cinfo)
1440{
1441 DestinationManager
1442 *destination;
1443
1444 destination=(DestinationManager *) cinfo->dest;
1445 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1446 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1447 destination->manager.next_output_byte=destination->buffer;
1448 destination->manager.free_in_buffer=MaxBufferExtent;
1449}
1450
1451static inline size_t MagickMin(const size_t x,const size_t y)
1452{
1453 if (x < y)
1454 return(x);
1455 return(y);
1456}
1457
1458static void TerminateDestination(j_compress_ptr cinfo)
1459{
1460 DestinationManager
1461 *destination;
1462
1463 destination=(DestinationManager *) cinfo->dest;
1464 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1465 {
1466 ssize_t
1467 count;
1468
1469 count=WriteBlob(destination->image,MaxBufferExtent-
1470 destination->manager.free_in_buffer,destination->buffer);
1471 if (count != (ssize_t)
1472 (MaxBufferExtent-destination->manager.free_in_buffer))
1473 ERREXIT(cinfo,JERR_FILE_WRITE);
1474 }
1475}
1476
1477static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1478{
1479 const char
1480 *name;
1481
1482 const StringInfo
1483 *profile;
1484
1485 MagickBooleanType
1486 iptc;
1487
1488 register long
1489 i;
1490
1491 size_t
1492 length;
1493
1494 StringInfo
1495 *custom_profile;
1496
1497 unsigned long
1498 tag_length;
1499
1500 /*
1501 Save image profile as a APP marker.
1502 */
1503 iptc=MagickFalse;
1504 custom_profile=AcquireStringInfo(65535L);
1505 ResetImageProfileIterator(image);
1506 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1507 {
1508 profile=GetImageProfile(image,name);
1509 if (LocaleCompare(name,"EXIF") == 0)
1510 for (i=0; i < (long) GetStringInfoLength(profile); i+=65533L)
1511 {
1512 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1513 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1514 (unsigned int) length);
1515 }
1516 if (LocaleCompare(name,"ICC") == 0)
1517 {
1518 register unsigned char
1519 *p;
1520
1521 tag_length=14;
1522 p=GetStringInfoDatum(custom_profile);
1523 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
1524 for (i=0; i < (long) GetStringInfoLength(profile); i+=65519L)
1525 {
1526 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
1527 p=GetStringInfoDatum(custom_profile);
1528 p[12]=(unsigned char) ((i/65519L)+1);
1529 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1530 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1531 length);
1532 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1533 custom_profile),(unsigned int) (length+tag_length));
1534 }
1535 }
1536 if (((LocaleCompare(name,"IPTC") == 0) ||
1537 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1538 {
1539 register unsigned char
1540 *p;
1541
1542 unsigned long
1543 roundup;
1544
1545 iptc=MagickTrue;
1546 p=GetStringInfoDatum(custom_profile);
1547 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1548 {
1549 (void) CopyMagickMemory(p,"Photoshop 3.0\0",14);
1550 tag_length=14;
1551 }
1552 else
1553 {
1554 (void) CopyMagickMemory(p,"Photoshop 3.0\08BIM\04\04\0\0\0\0",24);
1555 p[13]=0x00;
1556 p[24]=(unsigned char) (GetStringInfoLength(profile) >> 8);
1557 p[25]=(unsigned char) (GetStringInfoLength(profile) & 0xff);
1558 tag_length=26;
1559 }
1560 for (i=0; i < (long) GetStringInfoLength(profile); i+=65500L)
1561 {
1562 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
1563 roundup=(unsigned long) (length & 0x01);
1564 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1565 length);
1566 if (roundup != 0)
1567 p[length+tag_length]='\0';
1568 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1569 custom_profile),(unsigned int) (length+tag_length+roundup));
1570 }
1571 }
1572 if (LocaleCompare(name,"XMP") == 0)
1573 {
1574 StringInfo
1575 *xmp_profile;
1576
1577 /*
1578 Add namespace to XMP profile.
1579 */
1580 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/");
1581 ConcatenateStringInfo(xmp_profile,profile);
1582 GetStringInfoDatum(xmp_profile)[28]='\0';
1583 for (i=0; i < (long) GetStringInfoLength(xmp_profile); i+=65533L)
1584 {
1585 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1586 jpeg_write_marker(jpeg_info,XML_MARKER,
1587 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1588 }
1589 xmp_profile=DestroyStringInfo(xmp_profile);
1590 }
1591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s profile: %lu bytes",
1592 name,(unsigned long) GetStringInfoLength(profile));
1593 name=GetNextImageProfile(image);
1594 }
1595 custom_profile=DestroyStringInfo(custom_profile);
1596}
1597
1598static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1599{
1600 DestinationManager
1601 *destination;
1602
1603 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1604 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1605 destination=(DestinationManager *) cinfo->dest;
1606 destination->manager.init_destination=InitializeDestination;
1607 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1608 destination->manager.term_destination=TerminateDestination;
1609 destination->image=image;
1610}
1611
1612static char **SamplingFactorToList(const char *text)
1613{
1614 char
1615 **textlist;
1616
1617 register char
1618 *q;
1619
1620 register const char
1621 *p;
1622
1623 register long
1624 i;
1625
1626 unsigned long
1627 lines;
1628
1629 if (text == (char *) NULL)
1630 return((char **) NULL);
1631 /*
1632 Convert string to an ASCII list.
1633 */
1634 lines=1;
1635 for (p=text; *p != '\0'; p++)
1636 if (*p == ',')
1637 lines++;
1638 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1639 sizeof(*textlist));
1640 if (textlist == (char **) NULL)
1641 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1642 p=text;
1643 for (i=0; i < (long) lines; i++)
1644 {
1645 for (q=(char *) p; *q != '\0'; q++)
1646 if (*q == ',')
1647 break;
1648 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1649 sizeof(*textlist[i]));
1650 if (textlist[i] == (char *) NULL)
1651 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1652 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1653 if (*q == '\r')
1654 q++;
1655 p=q+1;
1656 }
1657 textlist[i]=(char *) NULL;
1658 return(textlist);
1659}
1660
1661static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1662 Image *image)
1663{
1664 const char
1665 *option,
1666 *sampling_factor,
1667 *value;
1668
1669 ErrorManager
1670 error_manager;
1671
1672 JSAMPLE
1673 *jpeg_pixels;
1674
1675 JSAMPROW
1676 scanline[1];
1677
1678 long
1679 y;
1680
1681 MagickBooleanType
1682 status;
1683
1684 register JSAMPLE
1685 *q;
1686
1687 register long
1688 i;
1689
1690 struct jpeg_compress_struct
1691 jpeg_info;
1692
1693 struct jpeg_error_mgr
1694 jpeg_error;
1695
1696 /*
1697 Open image file.
1698 */
1699 assert(image_info != (const ImageInfo *) NULL);
1700 assert(image_info->signature == MagickSignature);
1701 assert(image != (Image *) NULL);
1702 assert(image->signature == MagickSignature);
1703 if (image->debug != MagickFalse)
1704 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1705 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1706 if (status == MagickFalse)
1707 return(status);
1708 /*
1709 Initialize JPEG parameters.
1710 */
1711 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1712 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1713 jpeg_info.client_data=(void *) image;
1714 jpeg_info.err=jpeg_std_error(&jpeg_error);
1715 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage;
1716 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
1717 error_manager.image=image;
1718 jpeg_pixels=(JSAMPLE *) NULL;
1719 if (setjmp(error_manager.error_recovery) != 0)
1720 {
1721 jpeg_destroy_compress(&jpeg_info);
1722 (void) CloseBlob(image);
1723 return(MagickFalse);
1724 }
1725 jpeg_info.client_data=(void *) &error_manager;
1726 jpeg_create_compress(&jpeg_info);
1727 JPEGDestinationManager(&jpeg_info,image);
1728 if ((image->columns != (unsigned int) image->columns) ||
1729 (image->rows != (unsigned int) image->rows))
1730 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1731 jpeg_info.image_width=(unsigned int) image->columns;
1732 jpeg_info.image_height=(unsigned int) image->rows;
1733 jpeg_info.input_components=3;
1734 jpeg_info.data_precision=8;
1735 jpeg_info.in_color_space=JCS_RGB;
1736 switch (image->colorspace)
1737 {
1738 case CMYKColorspace:
1739 {
1740 jpeg_info.input_components=4;
1741 jpeg_info.in_color_space=JCS_CMYK;
1742 break;
1743 }
1744 case YCbCrColorspace:
1745 case Rec601YCbCrColorspace:
1746 case Rec709YCbCrColorspace:
1747 {
1748 jpeg_info.in_color_space=JCS_YCbCr;
1749 break;
1750 }
1751 case GRAYColorspace:
1752 case Rec601LumaColorspace:
1753 case Rec709LumaColorspace:
1754 {
1755 jpeg_info.input_components=1;
1756 jpeg_info.in_color_space=JCS_GRAYSCALE;
1757 break;
1758 }
1759 default:
cristy9f396782009-12-21 01:37:45 +00001760 {
1761 if (image->colorspace != RGBColorspace)
1762 (void) TransformImageColorspace(image,RGBColorspace);
cristy3ed852e2009-09-05 21:47:34 +00001763 break;
cristy9f396782009-12-21 01:37:45 +00001764 }
cristy3ed852e2009-09-05 21:47:34 +00001765 }
1766 if ((image_info->type != TrueColorType) &&
1767 (IsGrayImage(image,&image->exception) != MagickFalse))
1768 {
1769 jpeg_info.input_components=1;
1770 jpeg_info.in_color_space=JCS_GRAYSCALE;
1771 }
1772 jpeg_set_defaults(&jpeg_info);
1773 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1774 jpeg_info.data_precision=8;
1775 else
1776 if (sizeof(JSAMPLE) > 1)
1777 jpeg_info.data_precision=12;
1778 jpeg_info.density_unit=(UINT8) 1;
1779 if (image->debug != MagickFalse)
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy06609ee2010-03-17 20:21:27 +00001781 "Image resolution: %ld,%ld",(long) ceil(image->x_resolution-0.5),
1782 (long) ceil(image->y_resolution-0.5));
cristy3ed852e2009-09-05 21:47:34 +00001783 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1784 {
1785 /*
1786 Set image resolution.
1787 */
1788 jpeg_info.write_JFIF_header=MagickTrue;
1789 jpeg_info.X_density=(UINT16) image->x_resolution;
1790 jpeg_info.Y_density=(UINT16) image->y_resolution;
1791 if (image->units == PixelsPerInchResolution)
1792 jpeg_info.density_unit=(UINT8) 1;
1793 if (image->units == PixelsPerCentimeterResolution)
1794 jpeg_info.density_unit=(UINT8) 2;
1795 }
1796 option=GetImageOption(image_info,"jpeg:dct-method");
1797 if (option != (const char *) NULL)
1798 switch (*option)
1799 {
1800 case 'D':
1801 case 'd':
1802 {
1803 if (LocaleCompare(option,"default") == 0)
1804 jpeg_info.dct_method=JDCT_DEFAULT;
1805 break;
1806 }
1807 case 'F':
1808 case 'f':
1809 {
1810 if (LocaleCompare(option,"fastest") == 0)
1811 jpeg_info.dct_method=JDCT_FASTEST;
1812 if (LocaleCompare(option,"float") == 0)
1813 jpeg_info.dct_method=JDCT_FLOAT;
1814 break;
1815 }
1816 case 'I':
1817 case 'i':
1818 {
1819 if (LocaleCompare(option,"ifast") == 0)
1820 jpeg_info.dct_method=JDCT_IFAST;
1821 if (LocaleCompare(option,"islow") == 0)
1822 jpeg_info.dct_method=JDCT_ISLOW;
1823 break;
1824 }
1825 }
1826 option=GetImageOption(image_info,"jpeg:optimize-coding");
1827 if (option != (const char *) NULL)
1828 {
1829 jpeg_info.optimize_coding=MagickFalse;
1830 if (IsMagickTrue(option) != MagickFalse)
1831 jpeg_info.optimize_coding=MagickTrue;
1832 }
1833 else
1834 {
1835 MagickSizeType
1836 length;
1837
1838 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1839 image->rows*sizeof(JSAMPLE);
1840 if (length == (MagickSizeType) ((size_t) length))
1841 {
1842 /*
1843 Perform optimization only if available memory resources permit it.
1844 */
1845 status=AcquireMagickResource(MemoryResource,length);
1846 if (status != MagickFalse)
1847 jpeg_info.optimize_coding=MagickTrue;
1848 RelinquishMagickResource(MemoryResource,length);
1849 }
1850 }
1851#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1852 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1853 (image_info->interlace != NoInterlace))
1854 {
1855 if (image->debug != MagickFalse)
1856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1857 "Interlace: progressive");
1858 jpeg_simple_progression(&jpeg_info);
1859 }
1860 else
1861 if (image->debug != MagickFalse)
1862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1863 "Interlace: non-progressive");
1864#else
1865 if (image->debug != MagickFalse)
1866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1867 "Interlace: nonprogressive");
1868#endif
cristy0adb4f92009-11-28 18:08:51 +00001869 option=GetImageOption(image_info,"jpeg:extent");
1870 if (option != (const char *) NULL)
1871 {
1872 Image
1873 *jpeg_image;
1874
1875 ImageInfo
1876 *jpeg_info;
1877
1878 jpeg_info=CloneImageInfo(image_info);
1879 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1880 if (jpeg_image != (Image *) NULL)
1881 {
1882 MagickSizeType
1883 extent;
1884
1885 size_t
cristy87e73ab2010-02-12 01:59:12 +00001886 maximum,
1887 minimum;
cristy0adb4f92009-11-28 18:08:51 +00001888
1889 /*
1890 Search for compression quality that does not exceed image extent.
1891 */
1892 jpeg_info->quality=0;
cristyf2f27272009-12-17 14:48:46 +00001893 extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00001894 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1895 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00001896 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00001897 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00001898 {
cristy87e73ab2010-02-12 01:59:12 +00001899 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy0adb4f92009-11-28 18:08:51 +00001900 status=WriteJPEGImage(jpeg_info,jpeg_image);
cristyc8d69b12010-02-13 19:06:26 +00001901 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00001902 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00001903 else
cristy87e73ab2010-02-12 01:59:12 +00001904 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00001905 }
1906 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00001907 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00001908 jpeg_image=DestroyImage(jpeg_image);
1909 }
1910 jpeg_info=DestroyImageInfo(jpeg_info);
1911 }
cristy3ed852e2009-09-05 21:47:34 +00001912 if ((image_info->compression != LosslessJPEGCompression) &&
1913 (image->quality <= 100))
1914 {
1915 if (image->quality == UndefinedCompressionQuality)
1916 jpeg_set_quality(&jpeg_info,92,MagickTrue);
1917 else
1918 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
1919 if (image->debug != MagickFalse)
1920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %lu",
1921 image->quality);
1922 }
1923 else
1924 {
1925#if !defined(C_LOSSLESS_SUPPORTED)
1926 jpeg_set_quality(&jpeg_info,100,MagickTrue);
1927 if (image->debug != MagickFalse)
1928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
1929#else
1930 if (image->quality < 100)
1931 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1932 CoderWarning,"LosslessToLossyJPEGConversion",image->filename);
1933 else
1934 {
1935 int
1936 point_transform,
1937 predictor;
1938
1939 predictor=image->quality/100; /* range 1-7 */
1940 point_transform=image->quality % 20; /* range 0-15 */
1941 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
1942 if (image->debug != MagickFalse)
1943 {
1944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1945 "Compression: lossless");
1946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1947 "Predictor: %d",predictor);
1948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1949 "Point Transform: %d",point_transform);
1950 }
1951 }
1952#endif
1953 }
1954 sampling_factor=(const char *) NULL;
1955 value=GetImageProperty(image,"jpeg:sampling-factor");
1956 if (value != (char *) NULL)
1957 {
1958 sampling_factor=value;
1959 if (image->debug != MagickFalse)
1960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1961 " Input sampling-factors=%s",sampling_factor);
1962 }
1963 if (image_info->sampling_factor != (char *) NULL)
1964 sampling_factor=image_info->sampling_factor;
1965 if (sampling_factor == (const char *) NULL)
1966 {
1967 if (image->quality >= 90)
1968 for (i=0; i < MAX_COMPONENTS; i++)
1969 {
1970 jpeg_info.comp_info[i].h_samp_factor=1;
1971 jpeg_info.comp_info[i].v_samp_factor=1;
1972 }
1973 }
1974 else
1975 {
1976 char
1977 **factors;
1978
1979 GeometryInfo
1980 geometry_info;
1981
1982 MagickStatusType
1983 flags;
1984
1985 /*
1986 Set sampling factor.
1987 */
1988 i=0;
1989 factors=SamplingFactorToList(sampling_factor);
1990 if (factors != (char **) NULL)
1991 {
1992 for (i=0; i < MAX_COMPONENTS; i++)
1993 {
1994 if (factors[i] == (char *) NULL)
1995 break;
1996 flags=ParseGeometry(factors[i],&geometry_info);
1997 if ((flags & SigmaValue) == 0)
1998 geometry_info.sigma=geometry_info.rho;
1999 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2000 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2001 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2002 }
2003 factors=(char **) RelinquishMagickMemory(factors);
2004 }
2005 for ( ; i < MAX_COMPONENTS; i++)
2006 {
2007 jpeg_info.comp_info[i].h_samp_factor=1;
2008 jpeg_info.comp_info[i].v_samp_factor=1;
2009 }
2010 }
2011 if (jpeg_info.input_components == 1)
2012 for (i=0; i < MAX_COMPONENTS; i++)
2013 {
2014 jpeg_info.comp_info[i].h_samp_factor=1;
2015 jpeg_info.comp_info[i].v_samp_factor=1;
2016 }
2017 jpeg_start_compress(&jpeg_info,MagickTrue);
2018 if (image->debug != MagickFalse)
2019 {
2020 if (image->storage_class == PseudoClass)
2021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2022 "Storage class: PseudoClass");
2023 else
2024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2025 "Storage class: DirectClass");
2026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %lu",
2027 image->depth);
2028 if (image->colors != 0)
2029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2030 "Number of colors: %lu",image->colors);
2031 else
2032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2033 "Number of colors: unspecified");
2034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2035 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2036 switch (image->colorspace)
2037 {
2038 case CMYKColorspace:
2039 {
2040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2041 "Storage class: DirectClass");
2042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2043 "Colorspace: CMYK");
2044 break;
2045 }
2046 case YCbCrColorspace:
2047 case Rec601YCbCrColorspace:
2048 case Rec709YCbCrColorspace:
2049 {
2050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2051 "Colorspace: YCbCr");
2052 break;
2053 }
2054 default:
2055 break;
2056 }
2057 switch (image->colorspace)
2058 {
2059 case CMYKColorspace:
2060 {
2061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2062 "Colorspace: CMYK");
2063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2064 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2065 jpeg_info.comp_info[0].h_samp_factor,
2066 jpeg_info.comp_info[0].v_samp_factor,
2067 jpeg_info.comp_info[1].h_samp_factor,
2068 jpeg_info.comp_info[1].v_samp_factor,
2069 jpeg_info.comp_info[2].h_samp_factor,
2070 jpeg_info.comp_info[2].v_samp_factor,
2071 jpeg_info.comp_info[3].h_samp_factor,
2072 jpeg_info.comp_info[3].v_samp_factor);
2073 break;
2074 }
2075 case GRAYColorspace:
2076 case Rec601LumaColorspace:
2077 case Rec709LumaColorspace:
2078 {
2079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2080 "Colorspace: GRAY");
2081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2082 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2083 jpeg_info.comp_info[0].v_samp_factor);
2084 break;
2085 }
2086 case RGBColorspace:
2087 {
2088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2089 "Image colorspace is RGB");
2090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2091 "Sampling factors: %dx%d,%dx%d,%dx%d",
2092 jpeg_info.comp_info[0].h_samp_factor,
2093 jpeg_info.comp_info[0].v_samp_factor,
2094 jpeg_info.comp_info[1].h_samp_factor,
2095 jpeg_info.comp_info[1].v_samp_factor,
2096 jpeg_info.comp_info[2].h_samp_factor,
2097 jpeg_info.comp_info[2].v_samp_factor);
2098 break;
2099 }
2100 case YCbCrColorspace:
2101 case Rec601YCbCrColorspace:
2102 case Rec709YCbCrColorspace:
2103 {
2104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2105 "Colorspace: YCbCr");
2106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2107 "Sampling factors: %dx%d,%dx%d,%dx%d",
2108 jpeg_info.comp_info[0].h_samp_factor,
2109 jpeg_info.comp_info[0].v_samp_factor,
2110 jpeg_info.comp_info[1].h_samp_factor,
2111 jpeg_info.comp_info[1].v_samp_factor,
2112 jpeg_info.comp_info[2].h_samp_factor,
2113 jpeg_info.comp_info[2].v_samp_factor);
2114 break;
2115 }
2116 default:
2117 {
2118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2119 image->colorspace);
2120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2121 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2122 jpeg_info.comp_info[0].h_samp_factor,
2123 jpeg_info.comp_info[0].v_samp_factor,
2124 jpeg_info.comp_info[1].h_samp_factor,
2125 jpeg_info.comp_info[1].v_samp_factor,
2126 jpeg_info.comp_info[2].h_samp_factor,
2127 jpeg_info.comp_info[2].v_samp_factor,
2128 jpeg_info.comp_info[3].h_samp_factor,
2129 jpeg_info.comp_info[3].v_samp_factor);
2130 break;
2131 }
2132 }
2133 }
2134 /*
2135 Write JPEG profiles.
2136 */
2137 value=GetImageProperty(image,"comment");
2138 if (value != (char *) NULL)
2139 for (i=0; i < (long) strlen(value); i+=65533L)
2140 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2141 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2142 if (image->profiles != (void *) NULL)
2143 WriteProfile(&jpeg_info,image);
2144 /*
2145 Convert MIFF to JPEG raster pixels.
2146 */
2147 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2148 jpeg_info.input_components*sizeof(*jpeg_pixels));
2149 if (jpeg_pixels == (JSAMPLE *) NULL)
2150 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2151 if (setjmp(error_manager.error_recovery) != 0)
2152 {
2153 jpeg_destroy_compress(&jpeg_info);
2154 if (jpeg_pixels != (unsigned char *) NULL)
2155 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2156 (void) CloseBlob(image);
2157 return(MagickFalse);
2158 }
2159 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002160 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002161 {
cristy3ed852e2009-09-05 21:47:34 +00002162 if ((jpeg_info.in_color_space == JCS_RGB) ||
2163 (jpeg_info.in_color_space == JCS_YCbCr))
2164 for (y=0; y < (long) image->rows; y++)
2165 {
2166 register const PixelPacket
2167 *p;
2168
2169 register long
2170 x;
2171
2172 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2173 if (p == (const PixelPacket *) NULL)
2174 break;
2175 q=jpeg_pixels;
2176 for (x=0; x < (long) image->columns; x++)
2177 {
cristyce70c172010-01-07 17:15:30 +00002178 *q++=(JSAMPLE) ScaleQuantumToChar(GetRedPixelComponent(p));
2179 *q++=(JSAMPLE) ScaleQuantumToChar(GetGreenPixelComponent(p));
2180 *q++=(JSAMPLE) ScaleQuantumToChar(GetBluePixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00002181 p++;
2182 }
2183 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2184 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2185 break;
2186 }
2187 else
cristye90d7402010-03-14 18:21:29 +00002188 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2189 for (y=0; y < (long) image->rows; y++)
2190 {
2191 register const PixelPacket
2192 *p;
2193
2194 register long
2195 x;
2196
2197 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2198 if (p == (const PixelPacket *) NULL)
2199 break;
2200 q=jpeg_pixels;
2201 for (x=0; x < (long) image->columns; x++)
2202 {
2203 *q++=(JSAMPLE) ScaleQuantumToChar(PixelIntensityToQuantum(p));
2204 p++;
2205 }
2206 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2207 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2208 break;
2209 }
2210 else
2211 for (y=0; y < (long) image->rows; y++)
2212 {
2213 register const IndexPacket
2214 *indexes;
2215
2216 register const PixelPacket
2217 *p;
2218
2219 register long
2220 x;
2221
2222 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2223 if (p == (const PixelPacket *) NULL)
2224 break;
2225 q=jpeg_pixels;
2226 indexes=GetVirtualIndexQueue(image);
2227 for (x=0; x < (long) image->columns; x++)
2228 {
2229 /*
2230 Convert DirectClass packets to contiguous CMYK scanlines.
2231 */
2232 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2233 GetRedPixelComponent(p))));
2234 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2235 GetGreenPixelComponent(p))));
2236 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2237 GetBluePixelComponent(p))));
2238 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2239 indexes[x])));
2240 p++;
2241 }
2242 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2243 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2244 break;
2245 }
2246 }
2247 else
2248 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
2249 for (y=0; y < (long) image->rows; y++)
2250 {
2251 register const PixelPacket
2252 *p;
2253
2254 register long
2255 x;
2256
2257 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2258 if (p == (const PixelPacket *) NULL)
2259 break;
2260 q=jpeg_pixels;
2261 for (x=0; x < (long) image->columns; x++)
2262 {
2263 *q++=(JSAMPLE) (ScaleQuantumToShort(PixelIntensityToQuantum(p)) >>
2264 4);
2265 p++;
2266 }
2267 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2268 if (SetImageProgress(image,SaveImageTag,y,image->rows) == MagickFalse)
2269 break;
2270 }
2271 else
2272 if ((jpeg_info.in_color_space == JCS_RGB) ||
2273 (jpeg_info.in_color_space == JCS_YCbCr))
2274 for (y=0; y < (long) image->rows; y++)
2275 {
2276 register const PixelPacket
2277 *p;
2278
2279 register long
2280 x;
2281
2282 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2283 if (p == (const PixelPacket *) NULL)
2284 break;
2285 q=jpeg_pixels;
2286 for (x=0; x < (long) image->columns; x++)
2287 {
2288 *q++=(JSAMPLE) (ScaleQuantumToShort(GetRedPixelComponent(p)) >>
2289 4);
2290 *q++=(JSAMPLE) (ScaleQuantumToShort(GetGreenPixelComponent(p)) >>
2291 4);
2292 *q++=(JSAMPLE) (ScaleQuantumToShort(GetBluePixelComponent(p)) >>
2293 4);
2294 p++;
2295 }
2296 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
2297 status=SetImageProgress(image,SaveImageTag,y,image->rows);
2298 if (status == MagickFalse)
2299 break;
2300 }
2301 else
cristy3ed852e2009-09-05 21:47:34 +00002302 for (y=0; y < (long) image->rows; y++)
2303 {
2304 register const IndexPacket
2305 *indexes;
2306
2307 register const PixelPacket
2308 *p;
2309
2310 register long
2311 x;
2312
2313 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
2314 if (p == (const PixelPacket *) NULL)
2315 break;
2316 q=jpeg_pixels;
2317 indexes=GetVirtualIndexQueue(image);
2318 for (x=0; x < (long) image->columns; x++)
2319 {
2320 /*
2321 Convert DirectClass packets to contiguous CMYK scanlines.
2322 */
cristye90d7402010-03-14 18:21:29 +00002323 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2324 GetRedPixelComponent(p)) >> 4));
2325 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2326 GetGreenPixelComponent(p)) >> 4));
2327 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
2328 GetBluePixelComponent(p)) >> 4));
2329 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(indexes[x]) >> 4));
cristy3ed852e2009-09-05 21:47:34 +00002330 p++;
2331 }
2332 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristye90d7402010-03-14 18:21:29 +00002333 status=SetImageProgress(image,SaveImageTag,y,image->rows);
2334 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002335 break;
2336 }
2337 if (y == (long) image->rows)
2338 jpeg_finish_compress(&jpeg_info);
2339 /*
2340 Relinquish resources.
2341 */
2342 jpeg_destroy_compress(&jpeg_info);
2343 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2344 (void) CloseBlob(image);
2345 return(MagickTrue);
2346}
2347#endif