blob: 5489429a82280cb8bd9ad13903369d2c61295da4 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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*/
cristy4c08aed2011-07-01 19:47:50 +000046#include "MagickCore/studio.h"
47#include "MagickCore/attribute.h"
48#include "MagickCore/blob.h"
49#include "MagickCore/blob-private.h"
50#include "MagickCore/cache.h"
51#include "MagickCore/color.h"
52#include "MagickCore/colormap-private.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colormap.h"
55#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000056#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000057#include "MagickCore/constitute.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/magick.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/module.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/profile.h"
73#include "MagickCore/property.h"
74#include "MagickCore/quantum-private.h"
75#include "MagickCore/resource_.h"
76#include "MagickCore/splay-tree.h"
77#include "MagickCore/static.h"
78#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
80#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000081#include <setjmp.h>
82#if defined(MAGICKCORE_JPEG_DELEGATE)
83#define JPEG_INTERNAL_OPTIONS
84#if defined(__MINGW32__)
85# define XMD_H 1 /* Avoid conflicting typedef for INT32 */
cristye7e40552010-04-24 21:34:22 +000086typedef unsigned char boolean;
cristyc6da28e2011-04-28 01:41:35 +000087#define HAVE_BOOLEAN
cristy3ed852e2009-09-05 21:47:34 +000088#endif
89#undef HAVE_STDLIB_H
90#include "jpeglib.h"
91#include "jerror.h"
92#endif
93
94/*
95 Define declarations.
96*/
97#define ICC_MARKER (JPEG_APP0+2)
98#define ICC_PROFILE "ICC_PROFILE"
99#define IPTC_MARKER (JPEG_APP0+13)
100#define XML_MARKER (JPEG_APP0+1)
101#define MaxBufferExtent 8192
102
103/*
104 Typedef declarations.
105*/
106#if defined(MAGICKCORE_JPEG_DELEGATE)
107typedef struct _DestinationManager
108{
109 struct jpeg_destination_mgr
110 manager;
111
112 Image
113 *image;
114
115 JOCTET
116 *buffer;
117} DestinationManager;
118
119typedef struct _ErrorManager
120{
cristy018f07f2011-09-04 21:15:19 +0000121 ExceptionInfo
122 *exception;
123
cristy3ed852e2009-09-05 21:47:34 +0000124 Image
125 *image;
126
cristyd28b1dd2011-05-14 20:30:38 +0000127 MagickBooleanType
128 finished;
129
cristy3ed852e2009-09-05 21:47:34 +0000130 jmp_buf
131 error_recovery;
132} ErrorManager;
133
134typedef struct _SourceManager
135{
136 struct jpeg_source_mgr
137 manager;
138
139 Image
140 *image;
141
142 JOCTET
143 *buffer;
144
145 boolean
146 start_of_blob;
147} SourceManager;
148#endif
149
150/*
151 Forward declarations.
152*/
153#if defined(MAGICKCORE_JPEG_DELEGATE)
154static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000155 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000156#endif
157
158/*
159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160% %
161% %
162% %
163% I s J P E G %
164% %
165% %
166% %
167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168%
169% IsJPEG() returns MagickTrue if the image format type, identified by the
170% magick string, is JPEG.
171%
172% The format of the IsJPEG method is:
173%
174% MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
175%
176% A description of each parameter follows:
177%
178% o magick: compare image format pattern against these bytes.
179%
180% o length: Specifies the length of the magick string.
181%
182*/
183static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
184{
185 if (length < 3)
186 return(MagickFalse);
187 if (memcmp(magick,"\377\330\377",3) == 0)
188 return(MagickTrue);
189 return(MagickFalse);
190}
191
192#if defined(MAGICKCORE_JPEG_DELEGATE)
193/*
194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195% %
196% %
197% %
198% R e a d J P E G I m a g e %
199% %
200% %
201% %
202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203%
204% ReadJPEGImage() reads a JPEG image file and returns it. It allocates
205% the memory necessary for the new Image structure and returns a pointer to
206% the new image.
207%
208% The format of the ReadJPEGImage method is:
209%
210% Image *ReadJPEGImage(const ImageInfo *image_info,
211% ExceptionInfo *exception)
212%
213% A description of each parameter follows:
214%
215% o image_info: the image info.
216%
217% o exception: return any errors or warnings in this structure.
218%
219*/
220
cristy3ed852e2009-09-05 21:47:34 +0000221static boolean FillInputBuffer(j_decompress_ptr cinfo)
222{
223 SourceManager
224 *source;
225
226 source=(SourceManager *) cinfo->src;
cristy524222d2011-04-25 00:37:06 +0000227 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
228 MaxBufferExtent,source->buffer);
cristy3ed852e2009-09-05 21:47:34 +0000229 if (source->manager.bytes_in_buffer == 0)
230 {
231 if (source->start_of_blob != 0)
232 ERREXIT(cinfo,JERR_INPUT_EMPTY);
233 WARNMS(cinfo,JWRN_JPEG_EOF);
234 source->buffer[0]=(JOCTET) 0xff;
235 source->buffer[1]=(JOCTET) JPEG_EOI;
236 source->manager.bytes_in_buffer=2;
237 }
238 source->manager.next_input_byte=source->buffer;
239 source->start_of_blob=FALSE;
240 return(TRUE);
241}
242
243static int GetCharacter(j_decompress_ptr jpeg_info)
244{
245 if (jpeg_info->src->bytes_in_buffer == 0)
246 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
247 jpeg_info->src->bytes_in_buffer--;
248 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
249}
250
251static void InitializeSource(j_decompress_ptr cinfo)
252{
253 SourceManager
254 *source;
255
256 source=(SourceManager *) cinfo->src;
257 source->start_of_blob=TRUE;
258}
259
cristye8dd1302009-11-11 02:45:03 +0000260static MagickBooleanType IsITUFaxImage(const Image *image)
261{
262 const StringInfo
263 *profile;
264
265 const unsigned char
266 *datum;
267
cristyace6aa42009-11-11 03:17:33 +0000268 profile=GetImageProfile(image,"8bim");
cristye8dd1302009-11-11 02:45:03 +0000269 if (profile == (const StringInfo *) NULL)
270 return(MagickFalse);
271 if (GetStringInfoLength(profile) < 5)
272 return(MagickFalse);
273 datum=GetStringInfoDatum(profile);
274 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
275 (datum[3] == 0x41) && (datum[4] == 0x58))
276 return(MagickTrue);
277 return(MagickFalse);
278}
279
cristy437c3932011-10-12 18:03:46 +0000280static void JPEGErrorHandler(j_common_ptr jpeg_info)
cristy3ed852e2009-09-05 21:47:34 +0000281{
cristyd28b1dd2011-05-14 20:30:38 +0000282 char
283 message[JMSG_LENGTH_MAX];
284
cristy3ed852e2009-09-05 21:47:34 +0000285 ErrorManager
286 *error_manager;
287
cristyc82a27b2011-10-21 01:07:16 +0000288 ExceptionInfo
289 *exception;
290
cristyd28b1dd2011-05-14 20:30:38 +0000291 Image
292 *image;
293
294 *message='\0';
cristy3ed852e2009-09-05 21:47:34 +0000295 error_manager=(ErrorManager *) jpeg_info->client_data;
cristyd28b1dd2011-05-14 20:30:38 +0000296 image=error_manager->image;
cristyc82a27b2011-10-21 01:07:16 +0000297 exception=error_manager->exception;
cristy86f33542011-05-21 22:58:33 +0000298 (jpeg_info->err->format_message)(jpeg_info,message);
cristyd28b1dd2011-05-14 20:30:38 +0000299 if (image->debug != MagickFalse)
cristy86f33542011-05-21 22:58:33 +0000300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
301 "[%s] JPEG Trace: \"%s\"",image->filename,message);
cristyd28b1dd2011-05-14 20:30:38 +0000302 if (error_manager->finished != MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000303 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
304 (char *) message,"`%s'",image->filename);
cristyd28b1dd2011-05-14 20:30:38 +0000305 else
cristyc82a27b2011-10-21 01:07:16 +0000306 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
307 (char *) message,"`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000308 longjmp(error_manager->error_recovery,1);
309}
310
cristyd28b1dd2011-05-14 20:30:38 +0000311static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
312{
313 char
314 message[JMSG_LENGTH_MAX];
315
316 ErrorManager
317 *error_manager;
318
cristy018f07f2011-09-04 21:15:19 +0000319 ExceptionInfo
320 *exception;
321
cristyd28b1dd2011-05-14 20:30:38 +0000322 Image
323 *image;
324
325 *message='\0';
326 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000327 exception=error_manager->exception;
cristyd28b1dd2011-05-14 20:30:38 +0000328 image=error_manager->image;
329 if (level < 0)
330 {
331 /*
332 Process warning message.
333 */
334 (jpeg_info->err->format_message)(jpeg_info,message);
335 if ((jpeg_info->err->num_warnings == 0) ||
336 (jpeg_info->err->trace_level >= 3))
337 ThrowBinaryException(CorruptImageWarning,(char *) message,
338 image->filename);
339 jpeg_info->err->num_warnings++;
340 }
341 else
342 if ((image->debug != MagickFalse) &&
343 (level >= jpeg_info->err->trace_level))
344 {
345 /*
346 Process trace message.
347 */
348 (jpeg_info->err->format_message)(jpeg_info,message);
349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
350 "[%s] JPEG Trace: \"%s\"",image->filename,message);
351 }
352 return(MagickTrue);
353}
354
cristy3ed852e2009-09-05 21:47:34 +0000355static boolean ReadComment(j_decompress_ptr jpeg_info)
356{
357 char
358 *comment;
359
360 ErrorManager
361 *error_manager;
362
cristy018f07f2011-09-04 21:15:19 +0000363 ExceptionInfo
364 *exception;
365
cristy3ed852e2009-09-05 21:47:34 +0000366 Image
367 *image;
368
369 register char
370 *p;
371
cristybb503372010-05-27 20:51:26 +0000372 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000373 i;
374
375 size_t
376 length;
377
378 /*
379 Determine length of comment.
380 */
381 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000382 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000383 image=error_manager->image;
cristybb503372010-05-27 20:51:26 +0000384 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000385 length+=GetCharacter(jpeg_info);
386 length-=2;
387 if (length <= 0)
388 return(MagickTrue);
389 comment=(char *) NULL;
cristy37e0b382011-06-07 13:31:21 +0000390 if (~length >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000391 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
392 sizeof(*comment));
393 if (comment == (char *) NULL)
394 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
395 image->filename);
396 /*
397 Read comment.
398 */
cristybb503372010-05-27 20:51:26 +0000399 i=(ssize_t) length-1;
cristy3ed852e2009-09-05 21:47:34 +0000400 for (p=comment; i-- >= 0; p++)
401 *p=(char) GetCharacter(jpeg_info);
402 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000403 (void) SetImageProperty(image,"comment",comment,exception);
cristy3ed852e2009-09-05 21:47:34 +0000404 comment=DestroyString(comment);
405 return(MagickTrue);
406}
407
408static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
409{
410 char
411 magick[12];
412
413 ErrorManager
414 *error_manager;
415
cristy018f07f2011-09-04 21:15:19 +0000416 ExceptionInfo
417 *exception;
418
cristy3ed852e2009-09-05 21:47:34 +0000419 Image
420 *image;
421
422 MagickBooleanType
423 status;
424
cristybb503372010-05-27 20:51:26 +0000425 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000426 i;
427
428 register unsigned char
429 *p;
430
431 size_t
432 length;
433
434 StringInfo
435 *icc_profile,
436 *profile;
437
438 /*
439 Read color profile.
440 */
cristybb503372010-05-27 20:51:26 +0000441 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000442 length+=(size_t) GetCharacter(jpeg_info);
443 length-=2;
444 if (length <= 14)
445 {
446 while (length-- > 0)
447 (void) GetCharacter(jpeg_info);
448 return(MagickTrue);
449 }
450 for (i=0; i < 12; i++)
451 magick[i]=(char) GetCharacter(jpeg_info);
452 if (LocaleCompare(magick,ICC_PROFILE) != 0)
453 {
454 /*
455 Not a ICC profile, return.
456 */
cristybb503372010-05-27 20:51:26 +0000457 for (i=0; i < (ssize_t) (length-12); i++)
cristy3ed852e2009-09-05 21:47:34 +0000458 (void) GetCharacter(jpeg_info);
459 return(MagickTrue);
460 }
461 (void) GetCharacter(jpeg_info); /* id */
462 (void) GetCharacter(jpeg_info); /* markers */
463 length-=14;
464 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000465 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000466 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000467 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000468 if (profile == (StringInfo *) NULL)
469 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
470 image->filename);
471 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000472 for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
cristy3ed852e2009-09-05 21:47:34 +0000473 *p++=(unsigned char) GetCharacter(jpeg_info);
474 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
475 if (icc_profile != (StringInfo *) NULL)
476 {
477 ConcatenateStringInfo(icc_profile,profile);
478 profile=DestroyStringInfo(profile);
479 }
480 else
481 {
cristyd15e6592011-10-15 00:13:06 +0000482 status=SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000483 profile=DestroyStringInfo(profile);
484 if (status == MagickFalse)
485 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
486 image->filename);
487 }
488 if (image->debug != MagickFalse)
489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000490 "Profile: ICC, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000491 return(MagickTrue);
492}
493
494static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
495{
496 char
497 magick[MaxTextExtent];
498
499 ErrorManager
500 *error_manager;
501
cristy018f07f2011-09-04 21:15:19 +0000502 ExceptionInfo
503 *exception;
504
cristy3ed852e2009-09-05 21:47:34 +0000505 Image
506 *image;
507
508 MagickBooleanType
509 status;
510
cristybb503372010-05-27 20:51:26 +0000511 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000512 i;
513
514 register unsigned char
515 *p;
516
517 size_t
518 length;
519
520 StringInfo
521 *iptc_profile,
522 *profile;
523
524 /*
525 Determine length of binary data stored here.
526 */
cristybb503372010-05-27 20:51:26 +0000527 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000528 length+=(size_t) GetCharacter(jpeg_info);
529 length-=2;
530 if (length <= 14)
531 {
532 while (length-- > 0)
533 (void) GetCharacter(jpeg_info);
534 return(MagickTrue);
535 }
536 /*
537 Validate that this was written as a Photoshop resource format slug.
538 */
539 for (i=0; i < 10; i++)
540 magick[i]=(char) GetCharacter(jpeg_info);
541 magick[10]='\0';
542 if (length <= 10)
543 return(MagickTrue);
544 length-=10;
545 if (LocaleCompare(magick,"Photoshop ") != 0)
546 {
547 /*
548 Not a IPTC profile, return.
549 */
cristybb503372010-05-27 20:51:26 +0000550 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000551 (void) GetCharacter(jpeg_info);
552 return(MagickTrue);
553 }
554 /*
555 Remove the version number.
556 */
557 for (i=0; i < 4; i++)
558 (void) GetCharacter(jpeg_info);
559 if (length <= 4)
560 return(MagickTrue);
561 length-=4;
562 if (length == 0)
563 return(MagickTrue);
564 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000565 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000566 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000567 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000568 if (profile == (StringInfo *) NULL)
569 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
570 image->filename);
571 p=GetStringInfoDatum(profile);
cristy109e5572010-09-16 18:38:17 +0000572 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000573 *p++=(unsigned char) GetCharacter(jpeg_info);
574 iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
575 if (iptc_profile != (StringInfo *) NULL)
576 {
577 ConcatenateStringInfo(iptc_profile,profile);
578 profile=DestroyStringInfo(profile);
579 }
580 else
581 {
cristyd15e6592011-10-15 00:13:06 +0000582 status=SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000583 profile=DestroyStringInfo(profile);
584 if (status == MagickFalse)
585 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
586 image->filename);
587 }
588 if (image->debug != MagickFalse)
589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000590 "Profile: iptc, %.20g bytes",(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000591 return(MagickTrue);
592}
593
594static boolean ReadProfile(j_decompress_ptr jpeg_info)
595{
596 char
597 name[MaxTextExtent];
598
cristye23ec9d2011-08-16 18:15:40 +0000599 const StringInfo
600 *previous_profile;
601
cristy3ed852e2009-09-05 21:47:34 +0000602 ErrorManager
603 *error_manager;
604
cristy018f07f2011-09-04 21:15:19 +0000605 ExceptionInfo
606 *exception;
607
cristy3ed852e2009-09-05 21:47:34 +0000608 Image
609 *image;
610
611 int
612 marker;
613
614 MagickBooleanType
615 status;
616
cristybb503372010-05-27 20:51:26 +0000617 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000618 i;
619
620 register unsigned char
621 *p;
622
623 size_t
624 length;
625
626 StringInfo
627 *profile;
628
629 /*
630 Read generic profile.
631 */
cristybb503372010-05-27 20:51:26 +0000632 length=(size_t) ((size_t) GetCharacter(jpeg_info) << 8);
cristy3ed852e2009-09-05 21:47:34 +0000633 length+=(size_t) GetCharacter(jpeg_info);
634 if (length <= 2)
635 return(MagickTrue);
636 length-=2;
637 marker=jpeg_info->unread_marker-JPEG_APP0;
cristyb51dff52011-05-19 16:55:47 +0000638 (void) FormatLocaleString(name,MaxTextExtent,"APP%d",marker);
cristy3ed852e2009-09-05 21:47:34 +0000639 error_manager=(ErrorManager *) jpeg_info->client_data;
cristy018f07f2011-09-04 21:15:19 +0000640 exception=error_manager->exception;
cristy3ed852e2009-09-05 21:47:34 +0000641 image=error_manager->image;
cristy8723e4b2011-09-01 13:11:19 +0000642 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000643 if (profile == (StringInfo *) NULL)
644 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
645 image->filename);
646 p=GetStringInfoDatum(profile);
cristy08f255a2011-08-17 01:23:06 +0000647 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +0000648 *p++=(unsigned char) GetCharacter(jpeg_info);
649 if (marker == 1)
650 {
651 p=GetStringInfoDatum(profile);
652 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0))
653 (void) CopyMagickString(name,"exif",MaxTextExtent);
654 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0))
655 {
cristybb503372010-05-27 20:51:26 +0000656 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000657 j;
658
659 /*
660 Extract namespace from XMP profile.
661 */
662 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000663 for (j=0; j < (ssize_t) GetStringInfoLength(profile); j++)
cristy3ed852e2009-09-05 21:47:34 +0000664 {
665 if (*p == '\0')
666 break;
667 p++;
668 }
cristybb503372010-05-27 20:51:26 +0000669 if (j < (ssize_t) GetStringInfoLength(profile))
cristy3ed852e2009-09-05 21:47:34 +0000670 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
671 (void) CopyMagickString(name,"xmp",MaxTextExtent);
672 }
673 }
cristye23ec9d2011-08-16 18:15:40 +0000674 previous_profile=GetImageProfile(image,name);
675 if (previous_profile != (const StringInfo *) NULL)
cristy08f255a2011-08-17 01:23:06 +0000676 {
cristy05c0c9a2011-09-05 23:16:13 +0000677 ssize_t
678 length;
679
680 length=GetStringInfoLength(profile);
cristy08f255a2011-08-17 01:23:06 +0000681 SetStringInfoLength(profile,GetStringInfoLength(profile)+
682 GetStringInfoLength(previous_profile));
cristy83ab3d82011-09-06 12:05:51 +0000683 (void) memmove(GetStringInfoDatum(profile)+
684 GetStringInfoLength(previous_profile),GetStringInfoDatum(profile),
685 length);
cristy08f255a2011-08-17 01:23:06 +0000686 (void) memcpy(GetStringInfoDatum(profile),
687 GetStringInfoDatum(previous_profile),
688 GetStringInfoLength(previous_profile));
689 }
cristyd15e6592011-10-15 00:13:06 +0000690 status=SetImageProfile(image,name,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000691 profile=DestroyStringInfo(profile);
692 if (status == MagickFalse)
693 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
694 image->filename);
695 if (image->debug != MagickFalse)
696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000697 "Profile: %s, %.20g bytes",name,(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000698 return(MagickTrue);
699}
700
cristyf2faecf2010-05-28 19:19:36 +0000701static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
cristy3ed852e2009-09-05 21:47:34 +0000702{
703 SourceManager
704 *source;
705
706 if (number_bytes <= 0)
707 return;
708 source=(SourceManager *) cinfo->src;
cristy4cb162a2010-05-30 03:04:47 +0000709 while (number_bytes > (long) source->manager.bytes_in_buffer)
cristy3ed852e2009-09-05 21:47:34 +0000710 {
cristyf2faecf2010-05-28 19:19:36 +0000711 number_bytes-=(long) source->manager.bytes_in_buffer;
cristy3ed852e2009-09-05 21:47:34 +0000712 (void) FillInputBuffer(cinfo);
713 }
cristy4cb162a2010-05-30 03:04:47 +0000714 source->manager.next_input_byte+=number_bytes;
715 source->manager.bytes_in_buffer-=number_bytes;
cristy3ed852e2009-09-05 21:47:34 +0000716}
717
718static void TerminateSource(j_decompress_ptr cinfo)
719{
720 cinfo=cinfo;
721}
722
723static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
724{
725 SourceManager
726 *source;
727
728 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
729 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
730 source=(SourceManager *) cinfo->src;
731 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
732 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
733 source=(SourceManager *) cinfo->src;
734 source->manager.init_source=InitializeSource;
735 source->manager.fill_input_buffer=FillInputBuffer;
736 source->manager.skip_input_data=SkipInputData;
737 source->manager.resync_to_restart=jpeg_resync_to_restart;
738 source->manager.term_source=TerminateSource;
739 source->manager.bytes_in_buffer=0;
740 source->manager.next_input_byte=NULL;
741 source->image=image;
742}
743
744static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
745 Image *image)
746{
747 image->quality=UndefinedCompressionQuality;
748#if defined(D_PROGRESSIVE_SUPPORTED)
749 if (image->compression == LosslessJPEGCompression)
750 {
751 image->quality=100;
752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
753 "Quality: 100 (lossless)");
754 }
755 else
756#endif
757 {
cristybb503372010-05-27 20:51:26 +0000758 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000759 j,
760 qvalue,
761 sum;
762
cristybb503372010-05-27 20:51:26 +0000763 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000764 i;
765
766 /*
767 Determine the JPEG compression quality from the quantization tables.
768 */
769 sum=0;
770 for (i=0; i < NUM_QUANT_TBLS; i++)
771 {
772 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
773 for (j=0; j < DCTSIZE2; j++)
774 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
775 }
776 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
777 (jpeg_info->quant_tbl_ptrs[1] != NULL))
778 {
cristybb503372010-05-27 20:51:26 +0000779 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000780 hash[101] =
781 {
782 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
783 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
784 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
785 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
786 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
787 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
788 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
789 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
790 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
791 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
792 0
793 },
794 sums[101] =
795 {
796 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
797 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
798 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
799 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
800 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
801 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
802 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
803 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
804 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
805 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
806 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
807 128, 0
808 };
809
cristybb503372010-05-27 20:51:26 +0000810 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000811 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
812 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
813 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
814 for (i=0; i < 100; i++)
815 {
816 if ((qvalue < hash[i]) && (sum < sums[i]))
817 continue;
cristyb6c017e2010-01-13 19:11:39 +0000818 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000819 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000820 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000822 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000823 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000824 break;
825 }
826 }
827 else
828 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
829 {
cristybb503372010-05-27 20:51:26 +0000830 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000831 hash[101] =
832 {
833 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
834 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
835 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
836 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
837 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
838 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
839 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
840 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
841 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
842 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
843 0
844 },
845 sums[101] =
846 {
847 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
848 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
849 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
850 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
851 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
852 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
853 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
854 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
855 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
856 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
857 667, 592, 518, 441, 369, 292, 221, 151, 86,
858 64, 0
859 };
860
cristybb503372010-05-27 20:51:26 +0000861 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
cristy3ed852e2009-09-05 21:47:34 +0000862 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
863 for (i=0; i < 100; i++)
864 {
865 if ((qvalue < hash[i]) && (sum < sums[i]))
866 continue;
cristyb6c017e2010-01-13 19:11:39 +0000867 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
cristybb503372010-05-27 20:51:26 +0000868 image->quality=(size_t) i+1;
cristy3ed852e2009-09-05 21:47:34 +0000869 if (image->debug != MagickFalse)
cristyb6c017e2010-01-13 19:11:39 +0000870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000871 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
cristyb6c017e2010-01-13 19:11:39 +0000872 (sum <= sums[i]) ? "exact" : "approximate");
cristy3ed852e2009-09-05 21:47:34 +0000873 break;
874 }
875 }
876 }
877}
878
cristyd15e6592011-10-15 00:13:06 +0000879static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000880{
881 char
882 sampling_factor[MaxTextExtent];
883
884 switch (jpeg_info->out_color_space)
885 {
886 case JCS_CMYK:
887 {
888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
cristyb51dff52011-05-19 16:55:47 +0000889 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000890 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
891 jpeg_info->comp_info[0].v_samp_factor,
892 jpeg_info->comp_info[1].h_samp_factor,
893 jpeg_info->comp_info[1].v_samp_factor,
894 jpeg_info->comp_info[2].h_samp_factor,
895 jpeg_info->comp_info[2].v_samp_factor,
896 jpeg_info->comp_info[3].h_samp_factor,
897 jpeg_info->comp_info[3].v_samp_factor);
cristy787d4352010-03-06 13:55:58 +0000898 break;
cristy3ed852e2009-09-05 21:47:34 +0000899 }
900 case JCS_GRAYSCALE:
901 {
902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
903 "Colorspace: GRAYSCALE");
cristyb51dff52011-05-19 16:55:47 +0000904 (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
cristy3ed852e2009-09-05 21:47:34 +0000905 jpeg_info->comp_info[0].h_samp_factor,
906 jpeg_info->comp_info[0].v_samp_factor);
907 break;
908 }
909 case JCS_RGB:
910 {
911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
cristyb51dff52011-05-19 16:55:47 +0000912 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000913 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
914 jpeg_info->comp_info[0].v_samp_factor,
915 jpeg_info->comp_info[1].h_samp_factor,
916 jpeg_info->comp_info[1].v_samp_factor,
917 jpeg_info->comp_info[2].h_samp_factor,
918 jpeg_info->comp_info[2].v_samp_factor);
919 break;
920 }
921 default:
922 {
923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
924 jpeg_info->out_color_space);
cristyb51dff52011-05-19 16:55:47 +0000925 (void) FormatLocaleString(sampling_factor,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000926 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
927 jpeg_info->comp_info[0].v_samp_factor,
928 jpeg_info->comp_info[1].h_samp_factor,
929 jpeg_info->comp_info[1].v_samp_factor,
930 jpeg_info->comp_info[2].h_samp_factor,
931 jpeg_info->comp_info[2].v_samp_factor,
932 jpeg_info->comp_info[3].h_samp_factor,
933 jpeg_info->comp_info[3].v_samp_factor);
934 break;
935 }
936 }
cristyd15e6592011-10-15 00:13:06 +0000937 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
938 exception);
cristye90d7402010-03-14 18:21:29 +0000939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
940 sampling_factor);
cristy3ed852e2009-09-05 21:47:34 +0000941}
942
943static Image *ReadJPEGImage(const ImageInfo *image_info,
944 ExceptionInfo *exception)
945{
946 char
947 value[MaxTextExtent];
948
cristycaec74e2009-09-14 02:20:04 +0000949 const char
cristy11151212009-09-14 13:10:15 +0000950 *option;
cristycaec74e2009-09-14 02:20:04 +0000951
cristy3ed852e2009-09-05 21:47:34 +0000952 ErrorManager
953 error_manager;
954
cristy3ed852e2009-09-05 21:47:34 +0000955 Image
956 *image;
957
cristy3ed852e2009-09-05 21:47:34 +0000958 JSAMPLE
959 *jpeg_pixels;
960
961 JSAMPROW
962 scanline[1];
963
964 MagickBooleanType
965 debug,
966 status;
967
968 MagickSizeType
969 number_pixels;
970
cristy4c08aed2011-07-01 19:47:50 +0000971 Quantum
972 index;
973
cristybb503372010-05-27 20:51:26 +0000974 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000975 i;
976
977 struct jpeg_decompress_struct
978 jpeg_info;
979
980 struct jpeg_error_mgr
981 jpeg_error;
982
983 register JSAMPLE
984 *p;
985
cristybb503372010-05-27 20:51:26 +0000986 size_t
cristy3ed852e2009-09-05 21:47:34 +0000987 precision,
988 units;
989
cristy524222d2011-04-25 00:37:06 +0000990 ssize_t
991 y;
992
cristy3ed852e2009-09-05 21:47:34 +0000993 /*
994 Open image file.
995 */
996 assert(image_info != (const ImageInfo *) NULL);
997 assert(image_info->signature == MagickSignature);
998 if (image_info->debug != MagickFalse)
999 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1000 image_info->filename);
1001 assert(exception != (ExceptionInfo *) NULL);
1002 assert(exception->signature == MagickSignature);
1003 debug=IsEventLogging();
cristyda16f162011-02-19 23:52:17 +00001004 (void) debug;
cristy9950d572011-10-01 18:22:35 +00001005 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001006 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1007 if (status == MagickFalse)
1008 {
1009 image=DestroyImageList(image);
1010 return((Image *) NULL);
1011 }
1012 /*
1013 Initialize JPEG parameters.
1014 */
cristy91044972011-04-22 14:21:16 +00001015 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001016 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1017 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1018 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001019 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy3ed852e2009-09-05 21:47:34 +00001020 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
1021 jpeg_pixels=(JSAMPLE *) NULL;
cristy018f07f2011-09-04 21:15:19 +00001022 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001023 error_manager.image=image;
1024 if (setjmp(error_manager.error_recovery) != 0)
1025 {
1026 jpeg_destroy_decompress(&jpeg_info);
1027 (void) CloseBlob(image);
1028 number_pixels=(MagickSizeType) image->columns*image->rows;
1029 if (number_pixels != 0)
1030 return(GetFirstImageInList(image));
1031 return(DestroyImage(image));
1032 }
1033 jpeg_info.client_data=(void *) &error_manager;
1034 jpeg_create_decompress(&jpeg_info);
1035 JPEGSourceManager(&jpeg_info,image);
1036 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment);
1037 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile);
1038 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1039 for (i=1; i < 16; i++)
1040 if ((i != 2) && (i != 13) && (i != 14))
1041 jpeg_set_marker_processor(&jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
cristy4cb162a2010-05-30 03:04:47 +00001042 i=(ssize_t) jpeg_read_header(&jpeg_info,MagickTrue);
cristy53215c82009-09-19 16:32:36 +00001043 if ((image_info->colorspace == YCbCrColorspace) ||
1044 (image_info->colorspace == Rec601YCbCrColorspace) ||
1045 (image_info->colorspace == Rec709YCbCrColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001046 jpeg_info.out_color_space=JCS_YCbCr;
cristye8dd1302009-11-11 02:45:03 +00001047 if (IsITUFaxImage(image) != MagickFalse)
1048 {
1049 image->colorspace=LabColorspace;
1050 jpeg_info.out_color_space=JCS_YCbCr;
1051 }
1052 else
1053 if (jpeg_info.out_color_space == JCS_CMYK)
1054 image->colorspace=CMYKColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001055 /*
1056 Set image resolution.
1057 */
1058 units=0;
1059 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) &&
1060 (jpeg_info.Y_density != 1))
1061 {
1062 image->x_resolution=(double) jpeg_info.X_density;
1063 image->y_resolution=(double) jpeg_info.Y_density;
cristybb503372010-05-27 20:51:26 +00001064 units=(size_t) jpeg_info.density_unit;
cristy3ed852e2009-09-05 21:47:34 +00001065 }
1066 if (units == 1)
1067 image->units=PixelsPerInchResolution;
1068 if (units == 2)
1069 image->units=PixelsPerCentimeterResolution;
1070 number_pixels=(MagickSizeType) image->columns*image->rows;
cristy11151212009-09-14 13:10:15 +00001071 option=GetImageOption(image_info,"jpeg:size");
1072 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001073 {
1074 double
1075 scale_factor;
1076
cristycaec74e2009-09-14 02:20:04 +00001077 GeometryInfo
1078 geometry_info;
1079
cristy0adb4f92009-11-28 18:08:51 +00001080 MagickStatusType
cristycaec74e2009-09-14 02:20:04 +00001081 flags;
1082
cristy3ed852e2009-09-05 21:47:34 +00001083 /*
cristycaec74e2009-09-14 02:20:04 +00001084 Scale the image.
cristy3ed852e2009-09-05 21:47:34 +00001085 */
cristy11151212009-09-14 13:10:15 +00001086 flags=ParseGeometry(option,&geometry_info);
cristycaec74e2009-09-14 02:20:04 +00001087 if ((flags & SigmaValue) == 0)
1088 geometry_info.sigma=geometry_info.rho;
cristy3ed852e2009-09-05 21:47:34 +00001089 jpeg_calc_output_dimensions(&jpeg_info);
1090 image->magick_columns=jpeg_info.output_width;
1091 image->magick_rows=jpeg_info.output_height;
cristycaec74e2009-09-14 02:20:04 +00001092 scale_factor=1.0;
1093 if (geometry_info.rho != 0.0)
1094 scale_factor=jpeg_info.output_width/geometry_info.rho;
1095 if ((geometry_info.sigma != 0.0) &&
1096 (scale_factor > (jpeg_info.output_height/geometry_info.sigma)))
1097 scale_factor=jpeg_info.output_height/geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +00001098 jpeg_info.scale_num=1U;
1099 jpeg_info.scale_denom=(unsigned int) scale_factor;
1100 jpeg_calc_output_dimensions(&jpeg_info);
1101 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00001102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1103 "Scale factor: %.20g",(double) scale_factor);
cristy3ed852e2009-09-05 21:47:34 +00001104 }
cristybb503372010-05-27 20:51:26 +00001105 precision=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001106#if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1107#if defined(D_LOSSLESS_SUPPORTED)
1108 image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ?
1109 JPEGInterlace : NoInterlace;
1110 image->compression=jpeg_info.process == JPROC_LOSSLESS ?
1111 LosslessJPEGCompression : JPEGCompression;
1112 if (jpeg_info.data_precision > 8)
1113 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1114 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1115 image->filename);
1116 if (jpeg_info.data_precision == 16)
1117 jpeg_info.data_precision=12;
1118#else
1119 image->interlace=jpeg_info.progressive_mode != 0 ? JPEGInterlace :
1120 NoInterlace;
1121 image->compression=JPEGCompression;
1122#endif
1123#else
1124 image->compression=JPEGCompression;
1125 image->interlace=JPEGInterlace;
1126#endif
anthony41906552011-10-07 12:15:27 +00001127 option=GetImageOption(image_info,"jpeg:colors");
1128 if (option != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001129 {
anthony41906552011-10-07 12:15:27 +00001130 /* Let the JPEG library quantize the image */
cristy3ed852e2009-09-05 21:47:34 +00001131 jpeg_info.quantize_colors=MagickTrue;
anthony41906552011-10-07 12:15:27 +00001132 jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
cristy3ed852e2009-09-05 21:47:34 +00001133 }
cristy1b8d4462009-10-27 13:46:56 +00001134 option=GetImageOption(image_info,"jpeg:block-smoothing");
1135 if (option != (const char *) NULL)
1136 {
1137 jpeg_info.do_block_smoothing=MagickFalse;
1138 if (IsMagickTrue(option) != MagickFalse)
1139 jpeg_info.do_block_smoothing=MagickTrue;
1140 }
cristy787d4352010-03-06 13:55:58 +00001141 option=GetImageOption(image_info,"jpeg:dct-method");
1142 if (option != (const char *) NULL)
1143 switch (*option)
1144 {
1145 case 'D':
1146 case 'd':
1147 {
1148 if (LocaleCompare(option,"default") == 0)
1149 jpeg_info.dct_method=JDCT_DEFAULT;
1150 break;
1151 }
1152 case 'F':
1153 case 'f':
1154 {
1155 if (LocaleCompare(option,"fastest") == 0)
1156 jpeg_info.dct_method=JDCT_FASTEST;
1157 if (LocaleCompare(option,"float") == 0)
1158 jpeg_info.dct_method=JDCT_FLOAT;
1159 break;
1160 }
1161 case 'I':
1162 case 'i':
1163 {
1164 if (LocaleCompare(option,"ifast") == 0)
1165 jpeg_info.dct_method=JDCT_IFAST;
1166 if (LocaleCompare(option,"islow") == 0)
1167 jpeg_info.dct_method=JDCT_ISLOW;
1168 break;
1169 }
1170 }
cristy1b8d4462009-10-27 13:46:56 +00001171 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1172 if (option != (const char *) NULL)
1173 {
1174 jpeg_info.do_fancy_upsampling=MagickFalse;
1175 if (IsMagickTrue(option) != MagickFalse)
1176 jpeg_info.do_fancy_upsampling=MagickTrue;
1177 }
cristy3ed852e2009-09-05 21:47:34 +00001178 (void) jpeg_start_decompress(&jpeg_info);
1179 image->columns=jpeg_info.output_width;
1180 image->rows=jpeg_info.output_height;
cristybb503372010-05-27 20:51:26 +00001181 image->depth=(size_t) jpeg_info.data_precision;
cristy3ed852e2009-09-05 21:47:34 +00001182 if (jpeg_info.out_color_space == JCS_YCbCr)
1183 image->colorspace=YCbCrColorspace;
1184 if (jpeg_info.out_color_space == JCS_CMYK)
1185 image->colorspace=CMYKColorspace;
anthony41906552011-10-07 12:15:27 +00001186 option=GetImageOption(image_info,"jpeg:colors");
1187 if (option != (const char *) NULL)
1188 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
1189 == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001190 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1191 if ((jpeg_info.output_components == 1) &&
1192 (jpeg_info.quantize_colors == MagickFalse))
1193 {
cristybb503372010-05-27 20:51:26 +00001194 size_t
cristy3ed852e2009-09-05 21:47:34 +00001195 colors;
1196
cristybb503372010-05-27 20:51:26 +00001197 colors=(size_t) GetQuantumRange(image->depth)+1;
cristy018f07f2011-09-04 21:15:19 +00001198 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001199 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1200 }
1201 if (image->debug != MagickFalse)
1202 {
1203 if (image->interlace != NoInterlace)
1204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1205 "Interlace: progressive");
1206 else
1207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1208 "Interlace: nonprogressive");
1209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1210 (int) jpeg_info.data_precision);
1211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1212 (int) jpeg_info.output_width,(int) jpeg_info.output_height);
1213 }
1214 JPEGSetImageQuality(&jpeg_info,image);
cristyd15e6592011-10-15 00:13:06 +00001215 JPEGSetImageSamplingFactor(&jpeg_info,image,exception);
cristyb51dff52011-05-19 16:55:47 +00001216 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00001217 jpeg_info.out_color_space);
cristyd15e6592011-10-15 00:13:06 +00001218 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
cristy3ed852e2009-09-05 21:47:34 +00001219 if (image_info->ping != MagickFalse)
1220 {
1221 jpeg_destroy_decompress(&jpeg_info);
1222 (void) CloseBlob(image);
1223 return(GetFirstImageInList(image));
1224 }
1225 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
1226 jpeg_info.output_components*sizeof(JSAMPLE));
1227 if (jpeg_pixels == (JSAMPLE *) NULL)
1228 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1229 /*
1230 Convert JPEG pixels to pixel packets.
1231 */
1232 if (setjmp(error_manager.error_recovery) != 0)
1233 {
1234 if (jpeg_pixels != (unsigned char *) NULL)
1235 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1236 jpeg_destroy_decompress(&jpeg_info);
1237 (void) CloseBlob(image);
1238 number_pixels=(MagickSizeType) image->columns*image->rows;
1239 if (number_pixels != 0)
1240 return(GetFirstImageInList(image));
1241 return(DestroyImage(image));
1242 }
1243 if (jpeg_info.quantize_colors != MagickFalse)
1244 {
cristybb503372010-05-27 20:51:26 +00001245 image->colors=(size_t) jpeg_info.actual_number_of_colors;
cristy3ed852e2009-09-05 21:47:34 +00001246 if (jpeg_info.out_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00001247 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001248 {
1249 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1250 image->colormap[i].green=image->colormap[i].red;
1251 image->colormap[i].blue=image->colormap[i].red;
cristy4c08aed2011-07-01 19:47:50 +00001252 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001253 }
1254 else
cristybb503372010-05-27 20:51:26 +00001255 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001256 {
1257 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]);
1258 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]);
1259 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]);
cristy4c08aed2011-07-01 19:47:50 +00001260 image->colormap[i].alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00001261 }
1262 }
1263 scanline[0]=(JSAMPROW) jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00001264 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001265 {
cristybb503372010-05-27 20:51:26 +00001266 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001267 x;
1268
cristy4c08aed2011-07-01 19:47:50 +00001269 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001270 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001271
1272 if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
1273 {
1274 (void) ThrowMagickException(exception,GetMagickModule(),
1275 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1276 continue;
1277 }
1278 p=jpeg_pixels;
1279 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001280 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001281 break;
cristy3ed852e2009-09-05 21:47:34 +00001282 if (jpeg_info.data_precision > 8)
1283 {
1284 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001285 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001286 {
cristybb503372010-05-27 20:51:26 +00001287 size_t
cristy3ed852e2009-09-05 21:47:34 +00001288 pixel;
1289
1290 if (precision != 16)
cristybb503372010-05-27 20:51:26 +00001291 pixel=(size_t) GETJSAMPLE(*p);
cristy3ed852e2009-09-05 21:47:34 +00001292 else
cristybb503372010-05-27 20:51:26 +00001293 pixel=(size_t) ((GETJSAMPLE(*p) ^ 0x80) << 4);
cristyc82a27b2011-10-21 01:07:16 +00001294 index=ConstrainColormapIndex(image,pixel,exception);
cristy4c08aed2011-07-01 19:47:50 +00001295 SetPixelIndex(image,index,q);
cristy101ab702011-10-13 13:06:32 +00001296 SetPixelPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001297 p++;
cristyed231572011-07-14 02:18:59 +00001298 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001299 }
1300 else
1301 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001302 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001303 {
cristy4c08aed2011-07-01 19:47:50 +00001304 SetPixelRed(image,ScaleShortToQuantum((unsigned char)
1305 (GETJSAMPLE(*p++) << 4)),q);
1306 SetPixelGreen(image,ScaleShortToQuantum((unsigned char)
1307 (GETJSAMPLE(*p++) << 4)),q);
1308 SetPixelBlue(image,ScaleShortToQuantum((unsigned char)
1309 (GETJSAMPLE(*p++) << 4)),q);
1310 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001311 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001312 }
1313 else
cristybb503372010-05-27 20:51:26 +00001314 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001315 {
cristy4c08aed2011-07-01 19:47:50 +00001316 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1317 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1318 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1319 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1320 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1321 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1322 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1323 (unsigned char) (GETJSAMPLE(*p++) << 4)),q);
1324 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001325 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001326 }
1327 }
1328 else
1329 if (jpeg_info.output_components == 1)
cristybb503372010-05-27 20:51:26 +00001330 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001331 {
cristyc82a27b2011-10-21 01:07:16 +00001332 index=ConstrainColormapIndex(image,(size_t) GETJSAMPLE(*p),exception);
cristy4c08aed2011-07-01 19:47:50 +00001333 SetPixelIndex(image,index,q);
cristy101ab702011-10-13 13:06:32 +00001334 SetPixelPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy3ed852e2009-09-05 21:47:34 +00001335 p++;
cristyed231572011-07-14 02:18:59 +00001336 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001337 }
1338 else
1339 if (image->colorspace != CMYKColorspace)
cristybb503372010-05-27 20:51:26 +00001340 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001341 {
cristy4c08aed2011-07-01 19:47:50 +00001342 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1343 GETJSAMPLE(*p++)),q);
1344 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1345 GETJSAMPLE(*p++)),q);
1346 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1347 GETJSAMPLE(*p++)),q);
1348 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001349 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001350 }
1351 else
cristybb503372010-05-27 20:51:26 +00001352 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001353 {
cristy4c08aed2011-07-01 19:47:50 +00001354 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1355 (unsigned char) GETJSAMPLE(*p++)),q);
1356 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1357 (unsigned char) GETJSAMPLE(*p++)),q);
1358 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1359 (unsigned char) GETJSAMPLE(*p++)),q);
1360 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1361 (unsigned char) GETJSAMPLE(*p++)),q);
1362 SetPixelAlpha(image,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +00001363 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001364 }
1365 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1366 break;
cristy524222d2011-04-25 00:37:06 +00001367 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1368 image->rows);
1369 if (status == MagickFalse)
cristyd28b1dd2011-05-14 20:30:38 +00001370 {
1371 jpeg_abort_decompress(&jpeg_info);
1372 break;
1373 }
cristy3ed852e2009-09-05 21:47:34 +00001374 }
cristyd28b1dd2011-05-14 20:30:38 +00001375 if (status != MagickFalse)
1376 {
1377 error_manager.finished=MagickTrue;
1378 if (setjmp(error_manager.error_recovery) == 0)
1379 (void) jpeg_finish_decompress(&jpeg_info);
1380 }
cristy3ed852e2009-09-05 21:47:34 +00001381 /*
1382 Free jpeg resources.
1383 */
cristy3ed852e2009-09-05 21:47:34 +00001384 jpeg_destroy_decompress(&jpeg_info);
1385 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
1386 (void) CloseBlob(image);
1387 return(GetFirstImageInList(image));
1388}
1389#endif
1390
1391/*
1392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1393% %
1394% %
1395% %
1396% R e g i s t e r J P E G I m a g e %
1397% %
1398% %
1399% %
1400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401%
1402% RegisterJPEGImage() adds properties for the JPEG image format to
1403% the list of supported formats. The properties include the image format
1404% tag, a method to read and/or write the format, whether the format
1405% supports the saving of more than one frame to the same file or blob,
1406% whether the format supports native in-memory I/O, and a brief
1407% description of the format.
1408%
1409% The format of the RegisterJPEGImage method is:
1410%
cristybb503372010-05-27 20:51:26 +00001411% size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001412%
1413*/
cristybb503372010-05-27 20:51:26 +00001414ModuleExport size_t RegisterJPEGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001415{
1416 char
1417 version[MaxTextExtent];
1418
1419 MagickInfo
1420 *entry;
1421
1422 static const char
cristy7138c592009-09-08 13:58:52 +00001423 description[] = "Joint Photographic Experts Group JFIF format";
cristy3ed852e2009-09-05 21:47:34 +00001424
1425 *version='\0';
1426#if defined(JPEG_LIB_VERSION)
cristyb51dff52011-05-19 16:55:47 +00001427 (void) FormatLocaleString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION);
cristy3ed852e2009-09-05 21:47:34 +00001428#endif
1429 entry=SetMagickInfo("JPEG");
1430 entry->thread_support=NoThreadSupport;
1431#if defined(MAGICKCORE_JPEG_DELEGATE)
1432 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1433 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1434#endif
1435 entry->magick=(IsImageFormatHandler *) IsJPEG;
1436 entry->adjoin=MagickFalse;
1437 entry->description=ConstantString(description);
1438 if (*version != '\0')
1439 entry->version=ConstantString(version);
1440 entry->module=ConstantString("JPEG");
1441 (void) RegisterMagickInfo(entry);
1442 entry=SetMagickInfo("JPG");
1443 entry->thread_support=NoThreadSupport;
1444#if defined(MAGICKCORE_JPEG_DELEGATE)
1445 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1446 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1447#endif
1448 entry->adjoin=MagickFalse;
1449 entry->description=ConstantString(description);
1450 if (*version != '\0')
1451 entry->version=ConstantString(version);
1452 entry->module=ConstantString("JPEG");
1453 (void) RegisterMagickInfo(entry);
1454 entry=SetMagickInfo("PJPEG");
1455 entry->thread_support=NoThreadSupport;
1456#if defined(MAGICKCORE_JPEG_DELEGATE)
1457 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1458 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1459#endif
1460 entry->adjoin=MagickFalse;
1461 entry->description=ConstantString(description);
1462 if (*version != '\0')
1463 entry->version=ConstantString(version);
1464 entry->module=ConstantString("JPEG");
1465 (void) RegisterMagickInfo(entry);
1466 return(MagickImageCoderSignature);
1467}
1468
1469/*
1470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1471% %
1472% %
1473% %
1474% U n r e g i s t e r J P E G I m a g e %
1475% %
1476% %
1477% %
1478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1479%
1480% UnregisterJPEGImage() removes format registrations made by the
1481% JPEG module from the list of supported formats.
1482%
1483% The format of the UnregisterJPEGImage method is:
1484%
1485% UnregisterJPEGImage(void)
1486%
1487*/
1488ModuleExport void UnregisterJPEGImage(void)
1489{
1490 (void) UnregisterMagickInfo("PJPG");
1491 (void) UnregisterMagickInfo("JPEG");
1492 (void) UnregisterMagickInfo("JPG");
1493}
1494
1495#if defined(MAGICKCORE_JPEG_DELEGATE)
1496/*
1497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1498% %
1499% %
1500% %
1501% W r i t e J P E G I m a g e %
1502% %
1503% %
1504% %
1505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1506%
1507% WriteJPEGImage() writes a JPEG image file and returns it. It
1508% allocates the memory necessary for the new Image structure and returns a
1509% pointer to the new image.
1510%
1511% The format of the WriteJPEGImage method is:
1512%
cristy91044972011-04-22 14:21:16 +00001513% MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001514% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001515%
1516% A description of each parameter follows:
1517%
1518% o image_info: the image info.
1519%
1520% o jpeg_image: The image.
1521%
cristy1e178e72011-08-28 19:44:34 +00001522% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00001523%
1524*/
1525
1526static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1527{
1528 DestinationManager
1529 *destination;
1530
1531 destination=(DestinationManager *) cinfo->dest;
1532 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1533 MaxBufferExtent,destination->buffer);
1534 if (destination->manager.free_in_buffer != MaxBufferExtent)
1535 ERREXIT(cinfo,JERR_FILE_WRITE);
1536 destination->manager.next_output_byte=destination->buffer;
1537 return(TRUE);
1538}
1539
1540static void InitializeDestination(j_compress_ptr cinfo)
1541{
1542 DestinationManager
1543 *destination;
1544
1545 destination=(DestinationManager *) cinfo->dest;
1546 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1547 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET));
1548 destination->manager.next_output_byte=destination->buffer;
1549 destination->manager.free_in_buffer=MaxBufferExtent;
1550}
1551
1552static inline size_t MagickMin(const size_t x,const size_t y)
1553{
1554 if (x < y)
1555 return(x);
1556 return(y);
1557}
1558
1559static void TerminateDestination(j_compress_ptr cinfo)
1560{
1561 DestinationManager
1562 *destination;
1563
1564 destination=(DestinationManager *) cinfo->dest;
1565 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1566 {
1567 ssize_t
1568 count;
1569
1570 count=WriteBlob(destination->image,MaxBufferExtent-
1571 destination->manager.free_in_buffer,destination->buffer);
1572 if (count != (ssize_t)
1573 (MaxBufferExtent-destination->manager.free_in_buffer))
1574 ERREXIT(cinfo,JERR_FILE_WRITE);
1575 }
1576}
1577
1578static void WriteProfile(j_compress_ptr jpeg_info,Image *image)
1579{
1580 const char
1581 *name;
1582
1583 const StringInfo
1584 *profile;
1585
1586 MagickBooleanType
1587 iptc;
1588
cristybb503372010-05-27 20:51:26 +00001589 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001590 i;
1591
1592 size_t
cristy524222d2011-04-25 00:37:06 +00001593 length,
1594 tag_length;
cristy3ed852e2009-09-05 21:47:34 +00001595
1596 StringInfo
1597 *custom_profile;
1598
cristy3ed852e2009-09-05 21:47:34 +00001599 /*
1600 Save image profile as a APP marker.
1601 */
1602 iptc=MagickFalse;
1603 custom_profile=AcquireStringInfo(65535L);
1604 ResetImageProfileIterator(image);
1605 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1606 {
cristy109e5572010-09-16 18:38:17 +00001607 register unsigned char
1608 *p;
1609
cristy3ed852e2009-09-05 21:47:34 +00001610 profile=GetImageProfile(image,name);
cristy109e5572010-09-16 18:38:17 +00001611 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001612 if (LocaleCompare(name,"EXIF") == 0)
cristybb503372010-05-27 20:51:26 +00001613 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001614 {
1615 length=MagickMin(GetStringInfoLength(profile)-i,65533L);
1616 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile)+i,
1617 (unsigned int) length);
1618 }
1619 if (LocaleCompare(name,"ICC") == 0)
1620 {
1621 register unsigned char
1622 *p;
1623
1624 tag_length=14;
cristy35ce5c32010-09-16 23:16:02 +00001625 p=GetStringInfoDatum(custom_profile);
cristy3ed852e2009-09-05 21:47:34 +00001626 (void) CopyMagickMemory(p,ICC_PROFILE,tag_length);
cristybb503372010-05-27 20:51:26 +00001627 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
cristy3ed852e2009-09-05 21:47:34 +00001628 {
1629 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
cristy3ed852e2009-09-05 21:47:34 +00001630 p[12]=(unsigned char) ((i/65519L)+1);
1631 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
1632 (void) CopyMagickMemory(p+tag_length,GetStringInfoDatum(profile)+i,
1633 length);
1634 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
1635 custom_profile),(unsigned int) (length+tag_length));
1636 }
1637 }
1638 if (((LocaleCompare(name,"IPTC") == 0) ||
1639 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
1640 {
cristybb503372010-05-27 20:51:26 +00001641 size_t
cristy3ed852e2009-09-05 21:47:34 +00001642 roundup;
1643
1644 iptc=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001645 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
cristy3ed852e2009-09-05 21:47:34 +00001646 {
1647 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
cristybb503372010-05-27 20:51:26 +00001648 roundup=(size_t) (length & 0x01);
cristy109e5572010-09-16 18:38:17 +00001649 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
1650 {
1651 (void) memcpy(p,"Photoshop 3.0 ",14);
1652 tag_length=14;
1653 }
1654 else
1655 {
1656 (void) CopyMagickMemory(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
1657 tag_length=26;
1658 p[24]=(unsigned char) (length >> 8);
1659 p[25]=(unsigned char) (length & 0xff);
1660 }
1661 p[13]=0x00;
1662 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
cristy3ed852e2009-09-05 21:47:34 +00001663 if (roundup != 0)
1664 p[length+tag_length]='\0';
1665 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
1666 custom_profile),(unsigned int) (length+tag_length+roundup));
1667 }
1668 }
1669 if (LocaleCompare(name,"XMP") == 0)
1670 {
1671 StringInfo
1672 *xmp_profile;
1673
1674 /*
1675 Add namespace to XMP profile.
1676 */
cristy0615f0e2011-10-12 11:36:46 +00001677 xmp_profile=StringToStringInfo("http://ns.adobe.com/xap/1.0/ ");
cristy3ed852e2009-09-05 21:47:34 +00001678 ConcatenateStringInfo(xmp_profile,profile);
1679 GetStringInfoDatum(xmp_profile)[28]='\0';
cristybb503372010-05-27 20:51:26 +00001680 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00001681 {
1682 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
1683 jpeg_write_marker(jpeg_info,XML_MARKER,
1684 GetStringInfoDatum(xmp_profile)+i,(unsigned int) length);
1685 }
1686 xmp_profile=DestroyStringInfo(xmp_profile);
1687 }
cristye8c25f92010-06-03 00:53:06 +00001688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1689 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001690 name=GetNextImageProfile(image);
1691 }
1692 custom_profile=DestroyStringInfo(custom_profile);
1693}
1694
1695static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
1696{
1697 DestinationManager
1698 *destination;
1699
1700 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
1701 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
1702 destination=(DestinationManager *) cinfo->dest;
1703 destination->manager.init_destination=InitializeDestination;
1704 destination->manager.empty_output_buffer=EmptyOutputBuffer;
1705 destination->manager.term_destination=TerminateDestination;
1706 destination->image=image;
1707}
1708
1709static char **SamplingFactorToList(const char *text)
1710{
1711 char
1712 **textlist;
1713
1714 register char
1715 *q;
1716
1717 register const char
1718 *p;
1719
cristybb503372010-05-27 20:51:26 +00001720 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001721 i;
1722
cristybb503372010-05-27 20:51:26 +00001723 size_t
cristy3ed852e2009-09-05 21:47:34 +00001724 lines;
1725
1726 if (text == (char *) NULL)
1727 return((char **) NULL);
1728 /*
1729 Convert string to an ASCII list.
1730 */
1731 lines=1;
1732 for (p=text; *p != '\0'; p++)
1733 if (*p == ',')
1734 lines++;
1735 textlist=(char **) AcquireQuantumMemory((size_t) lines+MaxTextExtent,
1736 sizeof(*textlist));
1737 if (textlist == (char **) NULL)
1738 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1739 p=text;
cristybb503372010-05-27 20:51:26 +00001740 for (i=0; i < (ssize_t) lines; i++)
cristy3ed852e2009-09-05 21:47:34 +00001741 {
1742 for (q=(char *) p; *q != '\0'; q++)
1743 if (*q == ',')
1744 break;
1745 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
1746 sizeof(*textlist[i]));
1747 if (textlist[i] == (char *) NULL)
1748 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
1749 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
1750 if (*q == '\r')
1751 q++;
1752 p=q+1;
1753 }
1754 textlist[i]=(char *) NULL;
1755 return(textlist);
1756}
1757
1758static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +00001759 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001760{
1761 const char
1762 *option,
1763 *sampling_factor,
1764 *value;
1765
1766 ErrorManager
1767 error_manager;
1768
1769 JSAMPLE
1770 *jpeg_pixels;
1771
1772 JSAMPROW
1773 scanline[1];
1774
cristy3ed852e2009-09-05 21:47:34 +00001775 MagickBooleanType
1776 status;
1777
1778 register JSAMPLE
1779 *q;
1780
cristybb503372010-05-27 20:51:26 +00001781 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001782 i;
1783
cristy524222d2011-04-25 00:37:06 +00001784 ssize_t
1785 y;
1786
cristy3ed852e2009-09-05 21:47:34 +00001787 struct jpeg_compress_struct
1788 jpeg_info;
1789
1790 struct jpeg_error_mgr
1791 jpeg_error;
1792
1793 /*
1794 Open image file.
1795 */
1796 assert(image_info != (const ImageInfo *) NULL);
1797 assert(image_info->signature == MagickSignature);
1798 assert(image != (Image *) NULL);
1799 assert(image->signature == MagickSignature);
1800 if (image->debug != MagickFalse)
1801 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001802 assert(exception != (ExceptionInfo *) NULL);
1803 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00001804 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001805 if (status == MagickFalse)
1806 return(status);
1807 /*
1808 Initialize JPEG parameters.
1809 */
cristy91044972011-04-22 14:21:16 +00001810 (void) ResetMagickMemory(&error_manager,0,sizeof(error_manager));
cristy3ed852e2009-09-05 21:47:34 +00001811 (void) ResetMagickMemory(&jpeg_info,0,sizeof(jpeg_info));
1812 (void) ResetMagickMemory(&jpeg_error,0,sizeof(jpeg_error));
1813 jpeg_info.client_data=(void *) image;
1814 jpeg_info.err=jpeg_std_error(&jpeg_error);
cristyd28b1dd2011-05-14 20:30:38 +00001815 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
cristy91044972011-04-22 14:21:16 +00001816 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
cristy018f07f2011-09-04 21:15:19 +00001817 error_manager.exception=exception;
cristy3ed852e2009-09-05 21:47:34 +00001818 error_manager.image=image;
1819 jpeg_pixels=(JSAMPLE *) NULL;
1820 if (setjmp(error_manager.error_recovery) != 0)
1821 {
1822 jpeg_destroy_compress(&jpeg_info);
1823 (void) CloseBlob(image);
1824 return(MagickFalse);
1825 }
1826 jpeg_info.client_data=(void *) &error_manager;
1827 jpeg_create_compress(&jpeg_info);
1828 JPEGDestinationManager(&jpeg_info,image);
1829 if ((image->columns != (unsigned int) image->columns) ||
1830 (image->rows != (unsigned int) image->rows))
1831 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1832 jpeg_info.image_width=(unsigned int) image->columns;
1833 jpeg_info.image_height=(unsigned int) image->rows;
1834 jpeg_info.input_components=3;
1835 jpeg_info.data_precision=8;
1836 jpeg_info.in_color_space=JCS_RGB;
1837 switch (image->colorspace)
1838 {
1839 case CMYKColorspace:
1840 {
1841 jpeg_info.input_components=4;
1842 jpeg_info.in_color_space=JCS_CMYK;
1843 break;
1844 }
1845 case YCbCrColorspace:
1846 case Rec601YCbCrColorspace:
1847 case Rec709YCbCrColorspace:
1848 {
1849 jpeg_info.in_color_space=JCS_YCbCr;
1850 break;
1851 }
1852 case GRAYColorspace:
1853 case Rec601LumaColorspace:
1854 case Rec709LumaColorspace:
1855 {
1856 jpeg_info.input_components=1;
1857 jpeg_info.in_color_space=JCS_GRAYSCALE;
1858 break;
1859 }
1860 default:
cristy9f396782009-12-21 01:37:45 +00001861 {
cristy510d06a2011-07-06 23:43:54 +00001862 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00001863 (void) TransformImageColorspace(image,RGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001864 break;
cristy9f396782009-12-21 01:37:45 +00001865 }
cristy3ed852e2009-09-05 21:47:34 +00001866 }
1867 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00001868 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001869 {
1870 jpeg_info.input_components=1;
1871 jpeg_info.in_color_space=JCS_GRAYSCALE;
1872 }
1873 jpeg_set_defaults(&jpeg_info);
1874 if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
1875 jpeg_info.data_precision=8;
1876 else
1877 if (sizeof(JSAMPLE) > 1)
1878 jpeg_info.data_precision=12;
1879 jpeg_info.density_unit=(UINT8) 1;
1880 if (image->debug != MagickFalse)
1881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001882 "Image resolution: %.20g,%.20g",floor(image->x_resolution+0.5),
1883 floor(image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001884 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
1885 {
1886 /*
1887 Set image resolution.
1888 */
1889 jpeg_info.write_JFIF_header=MagickTrue;
cristyd999ef62010-05-08 19:54:26 +00001890 jpeg_info.X_density=(UINT16) floor(image->x_resolution+0.5);
1891 jpeg_info.Y_density=(UINT16) floor(image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001892 if (image->units == PixelsPerInchResolution)
1893 jpeg_info.density_unit=(UINT8) 1;
1894 if (image->units == PixelsPerCentimeterResolution)
1895 jpeg_info.density_unit=(UINT8) 2;
1896 }
1897 option=GetImageOption(image_info,"jpeg:dct-method");
1898 if (option != (const char *) NULL)
1899 switch (*option)
1900 {
1901 case 'D':
1902 case 'd':
1903 {
1904 if (LocaleCompare(option,"default") == 0)
1905 jpeg_info.dct_method=JDCT_DEFAULT;
1906 break;
1907 }
1908 case 'F':
1909 case 'f':
1910 {
1911 if (LocaleCompare(option,"fastest") == 0)
1912 jpeg_info.dct_method=JDCT_FASTEST;
1913 if (LocaleCompare(option,"float") == 0)
1914 jpeg_info.dct_method=JDCT_FLOAT;
1915 break;
1916 }
1917 case 'I':
1918 case 'i':
1919 {
1920 if (LocaleCompare(option,"ifast") == 0)
1921 jpeg_info.dct_method=JDCT_IFAST;
1922 if (LocaleCompare(option,"islow") == 0)
1923 jpeg_info.dct_method=JDCT_ISLOW;
1924 break;
1925 }
1926 }
1927 option=GetImageOption(image_info,"jpeg:optimize-coding");
1928 if (option != (const char *) NULL)
1929 {
1930 jpeg_info.optimize_coding=MagickFalse;
1931 if (IsMagickTrue(option) != MagickFalse)
1932 jpeg_info.optimize_coding=MagickTrue;
1933 }
1934 else
1935 {
1936 MagickSizeType
1937 length;
1938
1939 length=(MagickSizeType) jpeg_info.input_components*image->columns*
1940 image->rows*sizeof(JSAMPLE);
1941 if (length == (MagickSizeType) ((size_t) length))
1942 {
1943 /*
1944 Perform optimization only if available memory resources permit it.
1945 */
1946 status=AcquireMagickResource(MemoryResource,length);
1947 if (status != MagickFalse)
1948 jpeg_info.optimize_coding=MagickTrue;
1949 RelinquishMagickResource(MemoryResource,length);
1950 }
1951 }
1952#if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
1953 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
1954 (image_info->interlace != NoInterlace))
1955 {
1956 if (image->debug != MagickFalse)
1957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1958 "Interlace: progressive");
1959 jpeg_simple_progression(&jpeg_info);
1960 }
1961 else
1962 if (image->debug != MagickFalse)
1963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1964 "Interlace: non-progressive");
1965#else
1966 if (image->debug != MagickFalse)
1967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1968 "Interlace: nonprogressive");
1969#endif
cristy0adb4f92009-11-28 18:08:51 +00001970 option=GetImageOption(image_info,"jpeg:extent");
1971 if (option != (const char *) NULL)
1972 {
1973 Image
1974 *jpeg_image;
1975
1976 ImageInfo
1977 *jpeg_info;
1978
1979 jpeg_info=CloneImageInfo(image_info);
cristy1e178e72011-08-28 19:44:34 +00001980 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy0adb4f92009-11-28 18:08:51 +00001981 if (jpeg_image != (Image *) NULL)
1982 {
1983 MagickSizeType
1984 extent;
1985
1986 size_t
cristy87e73ab2010-02-12 01:59:12 +00001987 maximum,
1988 minimum;
cristy0adb4f92009-11-28 18:08:51 +00001989
1990 /*
1991 Search for compression quality that does not exceed image extent.
1992 */
1993 jpeg_info->quality=0;
cristyf2f27272009-12-17 14:48:46 +00001994 extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
cristy0adb4f92009-11-28 18:08:51 +00001995 (void) DeleteImageOption(jpeg_info,"jpeg:extent");
1996 (void) AcquireUniqueFilename(jpeg_image->filename);
cristye0a316c2010-02-13 19:15:23 +00001997 maximum=101;
cristyc8d69b12010-02-13 19:06:26 +00001998 for (minimum=0; minimum != maximum; )
cristy0adb4f92009-11-28 18:08:51 +00001999 {
cristy87e73ab2010-02-12 01:59:12 +00002000 jpeg_image->quality=minimum+(maximum-minimum)/2;
cristy1e178e72011-08-28 19:44:34 +00002001 status=WriteJPEGImage(jpeg_info,jpeg_image,exception);
cristyc8d69b12010-02-13 19:06:26 +00002002 if (GetBlobSize(jpeg_image) <= extent)
cristy87e73ab2010-02-12 01:59:12 +00002003 minimum=jpeg_image->quality+1;
cristy0adb4f92009-11-28 18:08:51 +00002004 else
cristy87e73ab2010-02-12 01:59:12 +00002005 maximum=jpeg_image->quality-1;
cristy0adb4f92009-11-28 18:08:51 +00002006 }
2007 (void) RelinquishUniqueFileResource(jpeg_image->filename);
cristyc8d69b12010-02-13 19:06:26 +00002008 image->quality=minimum-1;
cristy0adb4f92009-11-28 18:08:51 +00002009 jpeg_image=DestroyImage(jpeg_image);
2010 }
2011 jpeg_info=DestroyImageInfo(jpeg_info);
2012 }
cristy3ed852e2009-09-05 21:47:34 +00002013 if ((image_info->compression != LosslessJPEGCompression) &&
2014 (image->quality <= 100))
2015 {
2016 if (image->quality == UndefinedCompressionQuality)
2017 jpeg_set_quality(&jpeg_info,92,MagickTrue);
2018 else
2019 jpeg_set_quality(&jpeg_info,(int) image->quality,MagickTrue);
2020 if (image->debug != MagickFalse)
cristye8c25f92010-06-03 00:53:06 +00002021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2022 (double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00002023 }
2024 else
2025 {
2026#if !defined(C_LOSSLESS_SUPPORTED)
2027 jpeg_set_quality(&jpeg_info,100,MagickTrue);
2028 if (image->debug != MagickFalse)
2029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2030#else
2031 if (image->quality < 100)
cristy1e178e72011-08-28 19:44:34 +00002032 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2033 "LosslessToLossyJPEGConversion",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00002034 else
2035 {
2036 int
2037 point_transform,
2038 predictor;
2039
2040 predictor=image->quality/100; /* range 1-7 */
2041 point_transform=image->quality % 20; /* range 0-15 */
2042 jpeg_simple_lossless(&jpeg_info,predictor,point_transform);
2043 if (image->debug != MagickFalse)
2044 {
2045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2046 "Compression: lossless");
2047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2048 "Predictor: %d",predictor);
2049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2050 "Point Transform: %d",point_transform);
2051 }
2052 }
2053#endif
2054 }
2055 sampling_factor=(const char *) NULL;
cristyd15e6592011-10-15 00:13:06 +00002056 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
cristy3ed852e2009-09-05 21:47:34 +00002057 if (value != (char *) NULL)
2058 {
2059 sampling_factor=value;
2060 if (image->debug != MagickFalse)
2061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2062 " Input sampling-factors=%s",sampling_factor);
2063 }
2064 if (image_info->sampling_factor != (char *) NULL)
2065 sampling_factor=image_info->sampling_factor;
2066 if (sampling_factor == (const char *) NULL)
2067 {
2068 if (image->quality >= 90)
2069 for (i=0; i < MAX_COMPONENTS; i++)
2070 {
2071 jpeg_info.comp_info[i].h_samp_factor=1;
2072 jpeg_info.comp_info[i].v_samp_factor=1;
2073 }
2074 }
2075 else
2076 {
2077 char
2078 **factors;
2079
2080 GeometryInfo
2081 geometry_info;
2082
2083 MagickStatusType
2084 flags;
2085
2086 /*
2087 Set sampling factor.
2088 */
2089 i=0;
2090 factors=SamplingFactorToList(sampling_factor);
2091 if (factors != (char **) NULL)
2092 {
2093 for (i=0; i < MAX_COMPONENTS; i++)
2094 {
2095 if (factors[i] == (char *) NULL)
2096 break;
2097 flags=ParseGeometry(factors[i],&geometry_info);
2098 if ((flags & SigmaValue) == 0)
2099 geometry_info.sigma=geometry_info.rho;
2100 jpeg_info.comp_info[i].h_samp_factor=(int) geometry_info.rho;
2101 jpeg_info.comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2102 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2103 }
2104 factors=(char **) RelinquishMagickMemory(factors);
2105 }
2106 for ( ; i < MAX_COMPONENTS; i++)
2107 {
2108 jpeg_info.comp_info[i].h_samp_factor=1;
2109 jpeg_info.comp_info[i].v_samp_factor=1;
2110 }
2111 }
2112 if (jpeg_info.input_components == 1)
2113 for (i=0; i < MAX_COMPONENTS; i++)
2114 {
2115 jpeg_info.comp_info[i].h_samp_factor=1;
2116 jpeg_info.comp_info[i].v_samp_factor=1;
2117 }
2118 jpeg_start_compress(&jpeg_info,MagickTrue);
2119 if (image->debug != MagickFalse)
2120 {
2121 if (image->storage_class == PseudoClass)
2122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2123 "Storage class: PseudoClass");
2124 else
2125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2126 "Storage class: DirectClass");
cristye8c25f92010-06-03 00:53:06 +00002127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2128 (double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00002129 if (image->colors != 0)
2130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002131 "Number of colors: %.20g",(double) image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002132 else
2133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2134 "Number of colors: unspecified");
2135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2136 "JPEG data precision: %d",(int) jpeg_info.data_precision);
2137 switch (image->colorspace)
2138 {
2139 case CMYKColorspace:
2140 {
2141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2142 "Storage class: DirectClass");
2143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2144 "Colorspace: CMYK");
2145 break;
2146 }
2147 case YCbCrColorspace:
2148 case Rec601YCbCrColorspace:
2149 case Rec709YCbCrColorspace:
2150 {
2151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2152 "Colorspace: YCbCr");
2153 break;
2154 }
2155 default:
2156 break;
2157 }
2158 switch (image->colorspace)
2159 {
2160 case CMYKColorspace:
2161 {
2162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2163 "Colorspace: CMYK");
2164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2165 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2166 jpeg_info.comp_info[0].h_samp_factor,
2167 jpeg_info.comp_info[0].v_samp_factor,
2168 jpeg_info.comp_info[1].h_samp_factor,
2169 jpeg_info.comp_info[1].v_samp_factor,
2170 jpeg_info.comp_info[2].h_samp_factor,
2171 jpeg_info.comp_info[2].v_samp_factor,
2172 jpeg_info.comp_info[3].h_samp_factor,
2173 jpeg_info.comp_info[3].v_samp_factor);
2174 break;
2175 }
2176 case GRAYColorspace:
2177 case Rec601LumaColorspace:
2178 case Rec709LumaColorspace:
2179 {
2180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2181 "Colorspace: GRAY");
2182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2183 "Sampling factors: %dx%d",jpeg_info.comp_info[0].h_samp_factor,
2184 jpeg_info.comp_info[0].v_samp_factor);
2185 break;
2186 }
2187 case RGBColorspace:
2188 {
2189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2190 "Image colorspace is RGB");
2191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2192 "Sampling factors: %dx%d,%dx%d,%dx%d",
2193 jpeg_info.comp_info[0].h_samp_factor,
2194 jpeg_info.comp_info[0].v_samp_factor,
2195 jpeg_info.comp_info[1].h_samp_factor,
2196 jpeg_info.comp_info[1].v_samp_factor,
2197 jpeg_info.comp_info[2].h_samp_factor,
2198 jpeg_info.comp_info[2].v_samp_factor);
2199 break;
2200 }
2201 case YCbCrColorspace:
2202 case Rec601YCbCrColorspace:
2203 case Rec709YCbCrColorspace:
2204 {
2205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2206 "Colorspace: YCbCr");
2207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2208 "Sampling factors: %dx%d,%dx%d,%dx%d",
2209 jpeg_info.comp_info[0].h_samp_factor,
2210 jpeg_info.comp_info[0].v_samp_factor,
2211 jpeg_info.comp_info[1].h_samp_factor,
2212 jpeg_info.comp_info[1].v_samp_factor,
2213 jpeg_info.comp_info[2].h_samp_factor,
2214 jpeg_info.comp_info[2].v_samp_factor);
2215 break;
2216 }
2217 default:
2218 {
2219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2220 image->colorspace);
2221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2222 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2223 jpeg_info.comp_info[0].h_samp_factor,
2224 jpeg_info.comp_info[0].v_samp_factor,
2225 jpeg_info.comp_info[1].h_samp_factor,
2226 jpeg_info.comp_info[1].v_samp_factor,
2227 jpeg_info.comp_info[2].h_samp_factor,
2228 jpeg_info.comp_info[2].v_samp_factor,
2229 jpeg_info.comp_info[3].h_samp_factor,
2230 jpeg_info.comp_info[3].v_samp_factor);
2231 break;
2232 }
2233 }
2234 }
2235 /*
2236 Write JPEG profiles.
2237 */
cristyd15e6592011-10-15 00:13:06 +00002238 value=GetImageProperty(image,"comment",exception);
cristy3ed852e2009-09-05 21:47:34 +00002239 if (value != (char *) NULL)
cristybb503372010-05-27 20:51:26 +00002240 for (i=0; i < (ssize_t) strlen(value); i+=65533L)
cristy3ed852e2009-09-05 21:47:34 +00002241 jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) value+i,
2242 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2243 if (image->profiles != (void *) NULL)
2244 WriteProfile(&jpeg_info,image);
2245 /*
2246 Convert MIFF to JPEG raster pixels.
2247 */
2248 jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,
2249 jpeg_info.input_components*sizeof(*jpeg_pixels));
2250 if (jpeg_pixels == (JSAMPLE *) NULL)
2251 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2252 if (setjmp(error_manager.error_recovery) != 0)
2253 {
2254 jpeg_destroy_compress(&jpeg_info);
2255 if (jpeg_pixels != (unsigned char *) NULL)
2256 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2257 (void) CloseBlob(image);
2258 return(MagickFalse);
2259 }
2260 scanline[0]=(JSAMPROW) jpeg_pixels;
cristye90d7402010-03-14 18:21:29 +00002261 if (jpeg_info.data_precision <= 8)
cristy3ed852e2009-09-05 21:47:34 +00002262 {
cristy3ed852e2009-09-05 21:47:34 +00002263 if ((jpeg_info.in_color_space == JCS_RGB) ||
2264 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002265 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
cristy4c08aed2011-07-01 19:47:50 +00002267 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002268 *p;
2269
cristybb503372010-05-27 20:51:26 +00002270 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002271 x;
2272
cristy1e178e72011-08-28 19:44:34 +00002273 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002274 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002275 break;
2276 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002277 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002278 {
cristy4c08aed2011-07-01 19:47:50 +00002279 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2280 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2281 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00002282 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002283 }
2284 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002285 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2286 image->rows);
2287 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002288 break;
2289 }
2290 else
cristye90d7402010-03-14 18:21:29 +00002291 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002292 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002293 {
cristy4c08aed2011-07-01 19:47:50 +00002294 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002295 *p;
2296
cristybb503372010-05-27 20:51:26 +00002297 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002298 x;
2299
cristy1e178e72011-08-28 19:44:34 +00002300 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002301 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002302 break;
2303 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002304 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002305 {
cristy4c08aed2011-07-01 19:47:50 +00002306 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristyed231572011-07-14 02:18:59 +00002307 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002308 }
2309 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002310 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2311 image->rows);
2312 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002313 break;
2314 }
2315 else
cristybb503372010-05-27 20:51:26 +00002316 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002317 {
cristy4c08aed2011-07-01 19:47:50 +00002318 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002319 *p;
2320
cristybb503372010-05-27 20:51:26 +00002321 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002322 x;
2323
cristy1e178e72011-08-28 19:44:34 +00002324 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002325 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002326 break;
2327 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002328 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002329 {
2330 /*
2331 Convert DirectClass packets to contiguous CMYK scanlines.
2332 */
2333 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002334 GetPixelRed(image,p))));
cristye90d7402010-03-14 18:21:29 +00002335 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002336 GetPixelGreen(image,p))));
cristye90d7402010-03-14 18:21:29 +00002337 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002338 GetPixelBlue(image,p))));
cristye90d7402010-03-14 18:21:29 +00002339 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
cristy4c08aed2011-07-01 19:47:50 +00002340 GetPixelBlack(image,p))));
cristyed231572011-07-14 02:18:59 +00002341 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002342 }
2343 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002344 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2345 image->rows);
2346 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002347 break;
2348 }
2349 }
2350 else
2351 if (jpeg_info.in_color_space == JCS_GRAYSCALE)
cristybb503372010-05-27 20:51:26 +00002352 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002353 {
cristy4c08aed2011-07-01 19:47:50 +00002354 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002355 *p;
2356
cristybb503372010-05-27 20:51:26 +00002357 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002358 x;
2359
cristy1e178e72011-08-28 19:44:34 +00002360 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002361 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002362 break;
2363 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002364 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002365 {
cristy4c08aed2011-07-01 19:47:50 +00002366 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelIntensity(image,p)) >>
cristye90d7402010-03-14 18:21:29 +00002367 4);
cristyed231572011-07-14 02:18:59 +00002368 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002369 }
2370 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristy524222d2011-04-25 00:37:06 +00002371 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2372 image->rows);
2373 if (status == MagickFalse)
cristye90d7402010-03-14 18:21:29 +00002374 break;
2375 }
2376 else
2377 if ((jpeg_info.in_color_space == JCS_RGB) ||
2378 (jpeg_info.in_color_space == JCS_YCbCr))
cristybb503372010-05-27 20:51:26 +00002379 for (y=0; y < (ssize_t) image->rows; y++)
cristye90d7402010-03-14 18:21:29 +00002380 {
cristy4c08aed2011-07-01 19:47:50 +00002381 register const Quantum
cristye90d7402010-03-14 18:21:29 +00002382 *p;
2383
cristybb503372010-05-27 20:51:26 +00002384 register ssize_t
cristye90d7402010-03-14 18:21:29 +00002385 x;
2386
cristy1e178e72011-08-28 19:44:34 +00002387 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002388 if (p == (const Quantum *) NULL)
cristye90d7402010-03-14 18:21:29 +00002389 break;
2390 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002391 for (x=0; x < (ssize_t) image->columns; x++)
cristye90d7402010-03-14 18:21:29 +00002392 {
cristy4c08aed2011-07-01 19:47:50 +00002393 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p)) >> 4);
2394 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p)) >> 4);
2395 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p)) >> 4);
cristyed231572011-07-14 02:18:59 +00002396 p+=GetPixelChannels(image);
cristye90d7402010-03-14 18:21:29 +00002397 }
2398 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002399 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002400 image->rows);
cristye90d7402010-03-14 18:21:29 +00002401 if (status == MagickFalse)
2402 break;
2403 }
2404 else
cristybb503372010-05-27 20:51:26 +00002405 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002406 {
cristy4c08aed2011-07-01 19:47:50 +00002407 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002408 *p;
2409
cristybb503372010-05-27 20:51:26 +00002410 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002411 x;
2412
cristy1e178e72011-08-28 19:44:34 +00002413 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002414 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002415 break;
2416 q=jpeg_pixels;
cristybb503372010-05-27 20:51:26 +00002417 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002418 {
2419 /*
2420 Convert DirectClass packets to contiguous CMYK scanlines.
2421 */
cristye90d7402010-03-14 18:21:29 +00002422 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002423 GetPixelRed(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002424 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002425 GetPixelGreen(image,p)) >> 4));
cristye90d7402010-03-14 18:21:29 +00002426 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002427 GetPixelBlue(image,p)) >> 4));
cristy524222d2011-04-25 00:37:06 +00002428 *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(
cristy4c08aed2011-07-01 19:47:50 +00002429 GetPixelBlack(image,p)) >> 4));
cristyed231572011-07-14 02:18:59 +00002430 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002431 }
2432 (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
cristycee97112010-05-28 00:44:52 +00002433 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +00002434 image->rows);
cristye90d7402010-03-14 18:21:29 +00002435 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002436 break;
2437 }
cristybb503372010-05-27 20:51:26 +00002438 if (y == (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00002439 jpeg_finish_compress(&jpeg_info);
2440 /*
2441 Relinquish resources.
2442 */
2443 jpeg_destroy_compress(&jpeg_info);
2444 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
2445 (void) CloseBlob(image);
2446 return(MagickTrue);
2447}
2448#endif