Powered by Pair ImageMagick logo
Image Magick
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | File List | Namespace Members | Data Fields | Globals | Related Pages

ImageMagick-6.1.1/coders/jpeg.c

Go to the documentation of this file.
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % JJJJJ PPPP EEEEE GGGG % 00007 % J P P E G % 00008 % J PPPP EEE G GG % 00009 % J J P E G G % 00010 % JJJ P EEEEE GGG % 00011 % % 00012 % % 00013 % Read/Write JPEG Image Format. % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1992 % 00018 % % 00019 % % 00020 % Copyright 1999-2004 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/www/Copyright.html % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % This software is based in part on the work of the Independent JPEG Group. 00037 % See ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz for copyright and 00038 % licensing restrictions. Blob support contributed by Glenn Randers-Pehrson. 00039 % 00040 % 00041 */ 00042 00043 /* 00044 Include declarations. 00045 */ 00046 #include "magick/studio.h" 00047 #include "magick/attribute.h" 00048 #include "magick/blob.h" 00049 #include "magick/blob_private.h" 00050 #include "magick/color.h" 00051 #include "magick/color_private.h" 00052 #include "magick/colorspace.h" 00053 #include "magick/constitute.h" 00054 #include "magick/error.h" 00055 #include "magick/error_private.h" 00056 #include "magick/geometry.h" 00057 #include "magick/image.h" 00058 #include "magick/image_private.h" 00059 #include "magick/list.h" 00060 #include "magick/log.h" 00061 #include "magick/magick.h" 00062 #include "magick/memory_.h" 00063 #include "magick/monitor.h" 00064 #include "magick/option.h" 00065 #include "magick/profile.h" 00066 #include "magick/resource_.h" 00067 #include "magick/static.h" 00068 #include "magick/string_.h" 00069 #include "magick/utility.h" 00070 #include <setjmp.h> 00071 #if defined(HasJPEG) 00072 #define JPEG_INTERNAL_OPTIONS 00073 #if defined(__MINGW32__) 00074 # define XMD_H 1 /* Avoid conflicting typedef for INT32 */ 00075 #endif 00076 #undef HAVE_STDLIB_H 00077 #include "jpeglib.h" 00078 #include "jerror.h" 00079 #endif 00080 00081 /* 00082 Define declarations. 00083 */ 00084 #define ICC_MARKER (JPEG_APP0+2) 00085 #define ICC_PROFILE "ICC_PROFILE" 00086 #define IPTC_MARKER (JPEG_APP0+13) 00087 #define XML_MARKER (JPEG_APP0+1) 00088 #define MaxBufferExtent 8192 00089 00090 /* 00091 Typedef declarations. 00092 */ 00093 #if defined(HasJPEG) 00094 typedef struct _DestinationManager 00095 { 00096 struct jpeg_destination_mgr 00097 manager; 00098 00099 Image 00100 *image; 00101 00102 JOCTET 00103 *buffer; 00104 } DestinationManager; 00105 00106 typedef struct _ErrorManager 00107 { 00108 Image 00109 *image; 00110 00111 jmp_buf 00112 error_recovery; 00113 } ErrorManager; 00114 00115 typedef struct _SourceManager 00116 { 00117 struct jpeg_source_mgr 00118 manager; 00119 00120 Image 00121 *image; 00122 00123 JOCTET 00124 *buffer; 00125 00126 boolean 00127 start_of_blob; 00128 } SourceManager; 00129 #endif 00130 00131 /* 00132 Forward declarations. 00133 */ 00134 #if defined(HasJPEG) 00135 static MagickBooleanType 00136 WriteJPEGImage(const ImageInfo *,Image *); 00137 #endif 00138 00139 /* 00140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00141 % % 00142 % % 00143 % % 00144 % I s J P E G % 00145 % % 00146 % % 00147 % % 00148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00149 % 00150 % IsJPEG() returns MagickTrue if the image format type, identified by the 00151 % magick string, is JPEG. 00152 % 00153 % The format of the IsJPEG method is: 00154 % 00155 % MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length) 00156 % 00157 % A description of each parameter follows: 00158 % 00159 % o magick: This string is generally the first few bytes of an image file 00160 % or blob. 00161 % 00162 % o length: Specifies the length of the magick string. 00163 % 00164 % 00165 */ 00166 static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length) 00167 { 00168 if (length < 3) 00169 return(MagickFalse); 00170 if (memcmp(magick,"\377\330\377",3) == 0) 00171 return(MagickTrue); 00172 return(MagickFalse); 00173 } 00174 00175 #if defined(HasJPEG) 00176 /* 00177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00178 % % 00179 % % 00180 % % 00181 % R e a d J P E G I m a g e % 00182 % % 00183 % % 00184 % % 00185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00186 % 00187 % ReadJPEGImage() reads a JPEG image file and returns it. It allocates 00188 % the memory necessary for the new Image structure and returns a pointer to 00189 % the new image. 00190 % 00191 % The format of the ReadJPEGImage method is: 00192 % 00193 % Image *ReadJPEGImage(const ImageInfo *image_info, 00194 % ExceptionInfo *exception) 00195 % 00196 % A description of each parameter follows: 00197 % 00198 % o image_info: The image info. 00199 % 00200 % o exception: return any errors or warnings in this structure. 00201 % 00202 % 00203 */ 00204 00205 static MagickBooleanType EmitMessage(j_common_ptr jpeg_info,int level) 00206 { 00207 char 00208 message[JMSG_LENGTH_MAX]; 00209 00210 ErrorManager 00211 *error_manager; 00212 00213 Image 00214 *image; 00215 00216 (jpeg_info->err->format_message)(jpeg_info,message); 00217 error_manager=(ErrorManager *) jpeg_info->client_data; 00218 image=error_manager->image; 00219 if (level < 0) 00220 { 00221 if ((jpeg_info->err->num_warnings == 0) || 00222 (jpeg_info->err->trace_level >= 3)) 00223 ThrowBinaryException(CorruptImageWarning,(char *) message, 00224 image->filename); 00225 jpeg_info->err->num_warnings++; 00226 } 00227 else 00228 if (jpeg_info->err->trace_level >= level) 00229 ThrowBinaryException(CoderError,(char *) message,image->filename); 00230 return(MagickTrue); 00231 } 00232 00233 static boolean FillInputBuffer(j_decompress_ptr cinfo) 00234 { 00235 SourceManager 00236 *source; 00237 00238 source=(SourceManager *) cinfo->src; 00239 source->manager.bytes_in_buffer=(size_t) 00240 ReadBlob(source->image,MaxBufferExtent,source->buffer); 00241 if (source->manager.bytes_in_buffer == 0) 00242 { 00243 if (source->start_of_blob != 0) 00244 ERREXIT(cinfo,JERR_INPUT_EMPTY); 00245 WARNMS(cinfo,JWRN_JPEG_EOF); 00246 source->buffer[0]=(JOCTET) 0xff; 00247 source->buffer[1]=(JOCTET) JPEG_EOI; 00248 source->manager.bytes_in_buffer=2; 00249 } 00250 source->manager.next_input_byte=source->buffer; 00251 source->start_of_blob=FALSE; 00252 return(TRUE); 00253 } 00254 00255 static int GetCharacter(j_decompress_ptr jpeg_info) 00256 { 00257 if (jpeg_info->src->bytes_in_buffer == 0) 00258 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info); 00259 jpeg_info->src->bytes_in_buffer--; 00260 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++)); 00261 } 00262 00263 static void InitializeSource(j_decompress_ptr cinfo) 00264 { 00265 SourceManager 00266 *source; 00267 00268 source=(SourceManager *) cinfo->src; 00269 source->start_of_blob=TRUE; 00270 } 00271 00272 static void JPEGErrorHandler(j_common_ptr jpeg_info) 00273 { 00274 ErrorManager 00275 *error_manager; 00276 00277 (void) EmitMessage(jpeg_info,0); 00278 error_manager=(ErrorManager *) jpeg_info->client_data; 00279 longjmp(error_manager->error_recovery,1); 00280 } 00281 00282 static boolean ReadComment(j_decompress_ptr jpeg_info) 00283 { 00284 char 00285 *comment; 00286 00287 ErrorManager 00288 *error_manager; 00289 00290 Image 00291 *image; 00292 00293 register char 00294 *p; 00295 00296 register long 00297 i; 00298 00299 size_t 00300 length; 00301 00302 /* 00303 Determine length of comment. 00304 */ 00305 error_manager=(ErrorManager *) jpeg_info->client_data; 00306 image=error_manager->image; 00307 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); 00308 length+=GetCharacter(jpeg_info); 00309 length-=2; 00310 if (length <= 0) 00311 return(MagickTrue); 00312 comment=(char *) AcquireMagickMemory(length+MaxTextExtent); 00313 if (comment == (char *) NULL) 00314 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00315 image->filename); 00316 /* 00317 Read comment. 00318 */ 00319 i=(long) length-1; 00320 for (p=comment; i-- >= 0; p++) 00321 *p=(char) GetCharacter(jpeg_info); 00322 *p='\0'; 00323 (void) SetImageAttribute(image,"Comment",comment); 00324 comment=(char *) RelinquishMagickMemory(comment); 00325 return(MagickTrue); 00326 } 00327 00328 static boolean ReadICCProfile(j_decompress_ptr jpeg_info) 00329 { 00330 char 00331 magick[12]; 00332 00333 ErrorManager 00334 *error_manager; 00335 00336 Image 00337 *image; 00338 00339 MagickBooleanType 00340 status; 00341 00342 register long 00343 i; 00344 00345 register unsigned char 00346 *p; 00347 00348 size_t 00349 length; 00350 00351 StringInfo 00352 *icc_profile, 00353 *profile; 00354 00355 /* 00356 Read color profile. 00357 */ 00358 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); 00359 length+=(size_t) GetCharacter(jpeg_info); 00360 length-=2; 00361 if (length <= 14) 00362 { 00363 while (length-- > 0) 00364 (void) GetCharacter(jpeg_info); 00365 return(MagickTrue); 00366 } 00367 for (i=0; i < 12; i++) 00368 magick[i]=(char) GetCharacter(jpeg_info); 00369 if (LocaleCompare(magick,ICC_PROFILE) != 0) 00370 { 00371 /* 00372 Not a ICC profile, return. 00373 */ 00374 for (i=0; i < (long) (length-12); i++) 00375 (void) GetCharacter(jpeg_info); 00376 return(MagickTrue); 00377 } 00378 (void) GetCharacter(jpeg_info); /* id */ 00379 (void) GetCharacter(jpeg_info); /* markers */ 00380 length-=14; 00381 error_manager=(ErrorManager *) jpeg_info->client_data; 00382 image=error_manager->image; 00383 profile=AcquireStringInfo(length); 00384 if (profile == (StringInfo *) NULL) 00385 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00386 image->filename); 00387 p=profile->datum; 00388 for (i=(long) profile->length-1; i >= 0; i--) 00389 *p++=(unsigned char) GetCharacter(jpeg_info); 00390 icc_profile=GetImageProfile(image,"icc"); 00391 if (icc_profile != (StringInfo *) NULL) 00392 { 00393 ConcatenateStringInfo(icc_profile,profile); 00394 profile=DestroyStringInfo(profile); 00395 } 00396 else 00397 { 00398 status=SetImageProfile(image,"icc",profile); 00399 profile=DestroyStringInfo(profile); 00400 if (status == MagickFalse) 00401 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00402 image->filename); 00403 } 00404 if (image->debug != MagickFalse) 00405 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00406 "Profile: ICC, %lu bytes",(unsigned long) length); 00407 return(MagickTrue); 00408 } 00409 00410 static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info) 00411 { 00412 char 00413 magick[MaxTextExtent]; 00414 00415 ErrorManager 00416 *error_manager; 00417 00418 Image 00419 *image; 00420 00421 MagickBooleanType 00422 status; 00423 00424 register long 00425 i; 00426 00427 register unsigned char 00428 *p; 00429 00430 size_t 00431 length, 00432 tag_length; 00433 00434 StringInfo 00435 *iptc_profile, 00436 *profile; 00437 00438 #ifdef GET_ONLY_IPTC_DATA 00439 unsigned char 00440 tag[MaxTextExtent]; 00441 #endif 00442 00443 /* 00444 Determine length of binary data stored here. 00445 */ 00446 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); 00447 length+=(size_t) GetCharacter(jpeg_info); 00448 if (length <= 2) 00449 return(MagickTrue); 00450 length-=2; 00451 tag_length=0; 00452 #ifdef GET_ONLY_IPTC_DATA 00453 *tag='\0'; 00454 #endif 00455 error_manager=(ErrorManager *) jpeg_info->client_data; 00456 image=error_manager->image; 00457 iptc_profile=GetImageProfile(image,"iptc"); 00458 if (iptc_profile == (StringInfo *) NULL) 00459 { 00460 #ifdef GET_ONLY_IPTC_DATA 00461 /* 00462 Find the beginning of the IPTC portion of the binary data. 00463 */ 00464 for (*tag='\0'; length > 0; ) 00465 { 00466 *tag=GetCharacter(jpeg_info); 00467 *(tag+1)=GetCharacter(jpeg_info); 00468 length-=2; 00469 if ((*tag == 0x1c) && (*(tag+1) == 0x02)) 00470 break; 00471 } 00472 tag_length=2; 00473 #else 00474 /* 00475 Validate that this was written as a Photoshop resource format slug. 00476 */ 00477 for (i=0; i < 10; i++) 00478 magick[i]=(char) GetCharacter(jpeg_info); 00479 magick[10]='\0'; 00480 if (length <= 10) 00481 return(MagickTrue); 00482 length-=10; 00483 if (LocaleCompare(magick,"Photoshop ") != 0) 00484 { 00485 /* 00486 Not a ICC profile, return. 00487 */ 00488 for (i=0; i < (long) length; i++) 00489 (void) GetCharacter(jpeg_info); 00490 return(MagickTrue); 00491 } 00492 /* 00493 Remove the version number. 00494 */ 00495 for (i=0; i < 4; i++) 00496 (void) GetCharacter(jpeg_info); 00497 if (length <= 4) 00498 return(MagickTrue); 00499 length-=4; 00500 tag_length=0; 00501 #endif 00502 } 00503 if (length == 0) 00504 return(MagickTrue); 00505 profile=AcquireStringInfo(length); 00506 if (profile == (StringInfo *) NULL) 00507 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00508 image->filename); 00509 p=profile->datum; 00510 for (i=(long) profile->length-1; i >= 0; i--) 00511 *p++=(unsigned char) GetCharacter(jpeg_info); 00512 iptc_profile=GetImageProfile(image,"iptc"); 00513 if (iptc_profile != (StringInfo *) NULL) 00514 { 00515 ConcatenateStringInfo(iptc_profile,profile); 00516 profile=DestroyStringInfo(profile); 00517 } 00518 else 00519 { 00520 status=SetImageProfile(image,"iptc",profile); 00521 profile=DestroyStringInfo(profile); 00522 if (status == MagickFalse) 00523 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00524 image->filename); 00525 } 00526 if (image->debug != MagickFalse) 00527 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00528 "Profile: iptc, %lu bytes",(unsigned long) length); 00529 return(MagickTrue); 00530 } 00531 00532 static boolean ReadProfile(j_decompress_ptr jpeg_info) 00533 { 00534 char 00535 name[MaxTextExtent]; 00536 00537 ErrorManager 00538 *error_manager; 00539 00540 Image 00541 *image; 00542 00543 int 00544 marker; 00545 00546 MagickBooleanType 00547 status; 00548 00549 register long 00550 i; 00551 00552 register unsigned char 00553 *p; 00554 00555 size_t 00556 length; 00557 00558 StringInfo 00559 *profile; 00560 00561 /* 00562 Read generic profile. 00563 */ 00564 length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); 00565 length+=(size_t) GetCharacter(jpeg_info); 00566 if (length <= 2) 00567 return(MagickTrue); 00568 length-=2; 00569 marker=jpeg_info->unread_marker-JPEG_APP0; 00570 error_manager=(ErrorManager *) jpeg_info->client_data; 00571 image=error_manager->image; 00572 (void) FormatMagickString(name,MaxTextExtent,"APP%d",marker); 00573 profile=AcquireStringInfo(length); 00574 if (profile == (StringInfo *) NULL) 00575 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00576 image->filename); 00577 p=profile->datum; 00578 for (i=(long) profile->length-1; i >= 0; i--) 00579 *p++=(unsigned char) GetCharacter(jpeg_info); 00580 if (marker == 1) 00581 { 00582 p=profile->datum; 00583 if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0)) 00584 (void) CopyMagickString(name,"exif",MaxTextExtent); 00585 if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0)) 00586 (void) CopyMagickString(name,"xmp",MaxTextExtent); 00587 } 00588 status=SetImageProfile(image,name,profile); 00589 profile=DestroyStringInfo(profile); 00590 if (status == MagickFalse) 00591 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00592 image->filename); 00593 if (image->debug != MagickFalse) 00594 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00595 "Profile: %s, %lu bytes",name,(unsigned long) length); 00596 return(MagickTrue); 00597 } 00598 00599 static void SkipInputData(j_decompress_ptr cinfo,long number_bytes) 00600 { 00601 SourceManager 00602 *source; 00603 00604 if (number_bytes <= 0) 00605 return; 00606 source=(SourceManager *) cinfo->src; 00607 while (number_bytes > (long) source->manager.bytes_in_buffer) 00608 { 00609 number_bytes-=(long) source->manager.bytes_in_buffer; 00610 (void) FillInputBuffer(cinfo); 00611 } 00612 source->manager.next_input_byte+=(size_t) number_bytes; 00613 source->manager.bytes_in_buffer-=(size_t) number_bytes; 00614 } 00615 00616 static void TerminateSource(j_decompress_ptr cinfo) 00617 { 00618 cinfo=cinfo; 00619 } 00620 00621 static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image) 00622 { 00623 SourceManager 00624 *source; 00625 00626 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) 00627 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager)); 00628 source=(SourceManager *) cinfo->src; 00629 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small) 00630 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET)); 00631 source=(SourceManager *) cinfo->src; 00632 source->manager.init_source=InitializeSource; 00633 source->manager.fill_input_buffer=FillInputBuffer; 00634 source->manager.skip_input_data=SkipInputData; 00635 source->manager.resync_to_restart=jpeg_resync_to_restart; 00636 source->manager.term_source=TerminateSource; 00637 source->manager.bytes_in_buffer=0; 00638 source->manager.next_input_byte=NULL; 00639 source->image=image; 00640 } 00641 00642 static Image *ReadJPEGImage(const ImageInfo *image_info, 00643 ExceptionInfo *exception) 00644 { 00645 char 00646 sampling_factors[MaxTextExtent], 00647 value[MaxTextExtent]; 00648 00649 ErrorManager 00650 error_manager; 00651 00652 Image 00653 *image; 00654 00655 long 00656 x, 00657 y; 00658 00659 JSAMPLE 00660 *jpeg_pixels; 00661 00662 JSAMPROW 00663 scanline[1]; 00664 00665 MagickBooleanType 00666 debug, 00667 status; 00668 00669 register IndexPacket 00670 *indexes; 00671 00672 register long 00673 i; 00674 00675 struct jpeg_decompress_struct 00676 jpeg_info; 00677 00678 struct jpeg_error_mgr 00679 jpeg_error; 00680 00681 register JSAMPLE 00682 *p; 00683 00684 register PixelPacket 00685 *q; 00686 00687 unsigned long 00688 index, 00689 number_pixels, 00690 units; 00691 00692 /* 00693 Open image file. 00694 */ 00695 assert(image_info != (const ImageInfo *) NULL); 00696 assert(image_info->signature == MagickSignature); 00697 if (image_info->debug != MagickFalse) 00698 (void) LogMagickEvent(TraceEvent,GetMagickModule(),image_info->filename); 00699 assert(exception != (ExceptionInfo *) NULL); 00700 assert(exception->signature == MagickSignature); 00701 debug=IsEventLogging(); 00702 image=AllocateImage(image_info); 00703 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 00704 if (status == MagickFalse) 00705 { 00706 DestroyImageList(image); 00707 return((Image *) NULL); 00708 } 00709 /* 00710 Initialize image structure. 00711 */ 00712 jpeg_info.err=jpeg_std_error(&jpeg_error); 00713 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage; 00714 jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler; 00715 jpeg_pixels=(JSAMPLE *) NULL; 00716 error_manager.image=image; 00717 if (setjmp(error_manager.error_recovery) != 0) 00718 { 00719 jpeg_destroy_decompress(&jpeg_info); 00720 InheritException(exception,&image->exception); 00721 CloseBlob(image); 00722 number_pixels=image->columns*image->rows; 00723 if (number_pixels != 0) 00724 return(GetFirstImageInList(image)); 00725 image=DestroyImage(image); 00726 return(image); 00727 } 00728 jpeg_info.client_data=(void *) &error_manager; 00729 jpeg_create_decompress(&jpeg_info); 00730 JPEGSourceManager(&jpeg_info,image); 00731 jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment); 00732 jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile); 00733 jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile); 00734 for (i=1; i < 16; i++) 00735 if ((i != 2) && (i != 13) && (i != 14)) 00736 jpeg_set_marker_processor(&jpeg_info,(int) i+JPEG_APP0,ReadProfile); 00737 i=jpeg_read_header(&jpeg_info,MagickTrue); 00738 if (jpeg_info.out_color_space == JCS_CMYK) 00739 image->colorspace=CMYKColorspace; 00740 /* 00741 Set image resolution. 00742 */ 00743 units=0; 00744 if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1U) && 00745 (jpeg_info.Y_density != 1U)) 00746 { 00747 image->x_resolution=(double) jpeg_info.X_density; 00748 image->y_resolution=(double) jpeg_info.Y_density; 00749 units=(unsigned long) jpeg_info.density_unit; 00750 } 00751 if (units == 1) 00752 image->units=PixelsPerInchResolution; 00753 if (units == 2) 00754 image->units=PixelsPerCentimeterResolution; 00755 number_pixels=image->columns*image->rows; 00756 if (number_pixels != 0) 00757 { 00758 double 00759 scale_factor; 00760 00761 /* 00762 Let the JPEG library subsample for us. 00763 */ 00764 jpeg_calc_output_dimensions(&jpeg_info); 00765 image->magick_columns=jpeg_info.output_width; 00766 image->magick_rows=jpeg_info.output_height; 00767 scale_factor=(double) jpeg_info.output_width/image->columns; 00768 if (scale_factor > ((double) jpeg_info.output_height/image->rows)) 00769 scale_factor=(double) jpeg_info.output_height/image->rows; 00770 jpeg_info.scale_denom=(unsigned int) scale_factor; 00771 jpeg_calc_output_dimensions(&jpeg_info); 00772 if (image->debug != MagickFalse) 00773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Scale factor: %ld", 00774 (long) scale_factor); 00775 } 00776 if (image_info->subrange != 0) 00777 { 00778 jpeg_info.scale_denom=(unsigned int) image_info->subrange; 00779 jpeg_calc_output_dimensions(&jpeg_info); 00780 } 00781 #if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED) 00782 #ifdef D_LOSSLESS_SUPPORTED 00783 image->interlace= 00784 jpeg_info.process == JPROC_PROGRESSIVE ? PlaneInterlace : NoInterlace; 00785 image->compression=jpeg_info.process == JPROC_LOSSLESS ? 00786 LosslessJPEGCompression : JPEGCompression; 00787 if (jpeg_info.data_precision > 8) 00788 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 00789 "12-bit JPEG not supported. Reducing pixel data to 8 bits", 00790 image->filename); 00791 #else 00792 image->interlace=jpeg_info.progressive_mode != 0 ? PlaneInterlace : 00793 NoInterlace; 00794 image->compression=JPEGCompression; 00795 #endif 00796 #else 00797 image->compression=JPEGCompression; 00798 image->interlace=PlaneInterlace; 00799 #endif 00800 if ((image_info->colors != 0) && (image_info->colors <= 256)) 00801 { 00802 /* 00803 Let the JPEG library quantize for us. 00804 */ 00805 jpeg_info.quantize_colors=MagickTrue; 00806 jpeg_info.desired_number_of_colors=(int) image_info->colors; 00807 if (AllocateImageColormap(image,image_info->colors) == MagickFalse) 00808 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 00809 } 00810 (void) jpeg_start_decompress(&jpeg_info); 00811 image->columns=jpeg_info.output_width; 00812 image->rows=jpeg_info.output_height; 00813 image->depth=(unsigned long) jpeg_info.data_precision; 00814 if ((jpeg_info.output_components == 1) && 00815 (jpeg_info.quantize_colors == MagickFalse)) 00816 { 00817 if (AllocateImageColormap(image,1UL << image->depth) == MagickFalse) 00818 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 00819 } 00820 if (image->debug != MagickFalse) 00821 { 00822 if (image->interlace == PlaneInterlace) 00823 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00824 "Interlace: progressive"); 00825 else 00826 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00827 "Interlace: nonprogressive"); 00828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d", 00829 (int) jpeg_info.data_precision); 00830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d", 00831 (int) jpeg_info.output_width,(int) jpeg_info.output_height); 00832 } 00833 image->quality=0; 00834 #ifdef D_LOSSLESS_SUPPORTED 00835 if (image->compression == LosslessJPEGCompression) 00836 { 00837 image->quality=100; 00838 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00839 "Quality: 100 (lossless)"); 00840 } 00841 else 00842 #endif 00843 { 00844 int 00845 hashval, 00846 sum; 00847 00848 /* 00849 Log the JPEG quality that was used for compression. 00850 */ 00851 sum=0; 00852 for (i=0; i < NUM_QUANT_TBLS; i++) 00853 { 00854 int 00855 j; 00856 00857 if (jpeg_info.quant_tbl_ptrs[i] != NULL) 00858 for (j=0; j < DCTSIZE2; j++) 00859 { 00860 UINT16 00861 *c; 00862 00863 c=jpeg_info.quant_tbl_ptrs[i]->quantval; 00864 sum+=c[j]; 00865 } 00866 } 00867 if ((jpeg_info.quant_tbl_ptrs[0] != NULL) && 00868 (jpeg_info.quant_tbl_ptrs[1] != NULL)) 00869 { 00870 int 00871 hash[]= 00872 { 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645, 00873 632, 623, 613, 607, 600, 594, 589, 585, 581, 571, 00874 555, 542, 529, 514, 494, 474, 457, 439, 424, 410, 00875 397, 386, 373, 364, 351, 341, 334, 324, 317, 309, 00876 299, 294, 287, 279, 274, 267, 262, 257, 251, 247, 00877 243, 237, 232, 227, 222, 217, 213, 207, 202, 198, 00878 192, 188, 183, 177, 173, 168, 163, 157, 153, 148, 00879 143, 139, 132, 128, 125, 119, 115, 108, 104, 99, 00880 94, 90, 84, 79, 74, 70, 64, 59, 55, 49, 00881 45, 40, 34, 30, 25, 20, 15, 11, 6, 4, 00882 0 00883 }, 00884 sums[]= 00885 { 32640,32635,32266,31495,30665,29804,29146,28599,28104,27670, 00886 27225,26725,26210,25716,25240,24789,24373,23946,23572,22846, 00887 21801,20842,19949,19121,18386,17651,16998,16349,15800,15247, 00888 14783,14321,13859,13535,13081,12702,12423,12056,11779,11513, 00889 11135,10955,10676,10392,10208, 9928, 9747, 9564, 9369, 9193, 00890 9017, 8822, 8639, 8458, 8270, 8084, 7896, 7710, 7527, 7347, 00891 7156, 6977, 6788, 6607, 6422, 6236, 6054, 5867, 5684, 5495, 00892 5305, 5128, 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 00893 3509, 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846, 00894 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201, 128, 00895 0 00896 }; 00897 00898 hashval=(int) (jpeg_info.quant_tbl_ptrs[0]->quantval[2]+ 00899 jpeg_info.quant_tbl_ptrs[0]->quantval[53]+ 00900 jpeg_info.quant_tbl_ptrs[1]->quantval[0]+ 00901 jpeg_info.quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]); 00902 for (i=0; i < 100; i++) 00903 { 00904 if ((hashval >= hash[i]) || (sum >= sums[i])) 00905 { 00906 image->quality=(unsigned long) i+1; 00907 if (image->debug != MagickFalse) 00908 { 00909 if ((hashval > hash[i]) || (sum > sums[i])) 00910 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00911 "Quality: %ld (approximate)",image->quality); 00912 else 00913 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00914 "Quality: %ld",image->quality); 00915 } 00916 break; 00917 } 00918 } 00919 } 00920 else 00921 if (jpeg_info.quant_tbl_ptrs[0] != NULL) 00922 { 00923 int 00924 bwhash[]= 00925 { 510, 505, 422, 380, 355, 338, 326, 318, 311, 305, 00926 300, 297, 293, 291, 288, 286, 284, 283, 281, 280, 00927 279, 278, 277, 273, 262, 251, 243, 233, 225, 218, 00928 211, 205, 198, 193, 186, 181, 177, 172, 168, 164, 00929 158, 156, 152, 148, 145, 142, 139, 136, 133, 131, 00930 129, 126, 123, 120, 118, 115, 113, 110, 107, 105, 00931 102, 100, 97, 94, 92, 89, 87, 83, 81, 79, 00932 76, 74, 70, 68, 66, 63, 61, 57, 55, 52, 00933 50, 48, 44, 42, 39, 37, 34, 31, 29, 26, 00934 24, 21, 18, 16, 13, 11, 8, 6, 3, 2, 00935 0 00936 }, 00937 bwsum[]= 00938 { 16320,16315,15946,15277,14655,14073,13623,13230,12859,12560, 00939 12240,11861,11456,11081,10714,10360,10027, 9679, 9368, 9056, 00940 8680, 8331, 7995, 7668, 7376, 7084, 6823, 6562, 6345, 6125, 00941 5939, 5756, 5571, 5421, 5240, 5086, 4976, 4829, 4719, 4616, 00942 4463, 4393, 4280, 4166, 4092, 3980, 3909, 3835, 3755, 3688, 00943 3621, 3541, 3467, 3396, 3323, 3247, 3170, 3096, 3021, 2952, 00944 2874, 2804, 2727, 2657, 2583, 2509, 2437, 2362, 2290, 2211, 00945 2136, 2068, 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 00946 1398, 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736, 00947 667, 592, 518, 441, 369, 292, 221, 151, 86, 64, 00948 0 00949 }; 00950 00951 hashval=(int) (jpeg_info.quant_tbl_ptrs[0]->quantval[2]+ 00952 jpeg_info.quant_tbl_ptrs[0]->quantval[53]); 00953 for (i=0; i < 100; i++) 00954 { 00955 if ((hashval >= bwhash[i]) || (sum >= bwsum[i])) 00956 { 00957 image->quality=(unsigned long) i+1; 00958 if (image->debug != MagickFalse) 00959 { 00960 if ((hashval > bwhash[i]) || (sum > bwsum[i])) 00961 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00962 "Quality: %ld (approximate)",i+1); 00963 else 00964 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00965 "Quality: %ld",i+1); 00966 } 00967 break; 00968 } 00969 } 00970 } 00971 } 00972 (void) FormatMagickString(value,MaxTextExtent,"%ld",(long) 00973 jpeg_info.out_color_space); 00974 (void) SetImageAttribute(image,"JPEG-Colorspace",value); 00975 /* 00976 Set image sampling factors. 00977 */ 00978 switch (jpeg_info.out_color_space) 00979 { 00980 case JCS_CMYK: 00981 { 00982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK"); 00983 (void) FormatMagickString(sampling_factors,MaxTextExtent, 00984 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info.comp_info[0].h_samp_factor, 00985 jpeg_info.comp_info[0].v_samp_factor, 00986 jpeg_info.comp_info[1].h_samp_factor, 00987 jpeg_info.comp_info[1].v_samp_factor, 00988 jpeg_info.comp_info[2].h_samp_factor, 00989 jpeg_info.comp_info[2].v_samp_factor, 00990 jpeg_info.comp_info[3].h_samp_factor, 00991 jpeg_info.comp_info[3].v_samp_factor); 00992 break; 00993 } 00994 case JCS_GRAYSCALE: 00995 { 00996 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 00997 "Colorspace: GRAYSCALE"); 00998 (void) FormatMagickString(sampling_factors,MaxTextExtent,"%dx%d", 00999 jpeg_info.comp_info[0].h_samp_factor, 01000 jpeg_info.comp_info[0].v_samp_factor); 01001 break; 01002 } 01003 case JCS_RGB: 01004 { 01005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB"); 01006 (void) FormatMagickString(sampling_factors,MaxTextExtent, 01007 "%dx%d,%dx%d,%dx%d",jpeg_info.comp_info[0].h_samp_factor, 01008 jpeg_info.comp_info[0].v_samp_factor, 01009 jpeg_info.comp_info[1].h_samp_factor, 01010 jpeg_info.comp_info[1].v_samp_factor, 01011 jpeg_info.comp_info[2].h_samp_factor, 01012 jpeg_info.comp_info[2].v_samp_factor); 01013 break; 01014 } 01015 default: 01016 { 01017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d", 01018 jpeg_info.out_color_space); 01019 (void) FormatMagickString(sampling_factors,MaxTextExtent, 01020 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info.comp_info[0].h_samp_factor, 01021 jpeg_info.comp_info[0].v_samp_factor, 01022 jpeg_info.comp_info[1].h_samp_factor, 01023 jpeg_info.comp_info[1].v_samp_factor, 01024 jpeg_info.comp_info[2].h_samp_factor, 01025 jpeg_info.comp_info[2].v_samp_factor, 01026 jpeg_info.comp_info[3].h_samp_factor, 01027 jpeg_info.comp_info[3].v_samp_factor); 01028 break; 01029 } 01030 } 01031 (void) SetImageAttribute(image,"JPEG-Sampling-factors",sampling_factors); 01032 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 01033 "Sampling Factors: %s",sampling_factors); 01034 if (image_info->ping != MagickFalse) 01035 { 01036 jpeg_destroy_decompress(&jpeg_info); 01037 CloseBlob(image); 01038 return(GetFirstImageInList(image)); 01039 } 01040 jpeg_pixels=(JSAMPLE *) AcquireMagickMemory((size_t) 01041 jpeg_info.output_components*image->columns*sizeof(JSAMPLE)); 01042 if (jpeg_pixels == (JSAMPLE *) NULL) 01043 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 01044 /* 01045 Convert JPEG pixels to pixel packets. 01046 */ 01047 if (setjmp(error_manager.error_recovery) != 0) 01048 { 01049 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); 01050 jpeg_destroy_decompress(&jpeg_info); 01051 InheritException(exception,&image->exception); 01052 CloseBlob(image); 01053 number_pixels=image->columns*image->rows; 01054 if (number_pixels != 0) 01055 return(GetFirstImageInList(image)); 01056 image=DestroyImage(image); 01057 return(image); 01058 } 01059 scanline[0]=(JSAMPROW) jpeg_pixels; 01060 for (y=0; y < (long) image->rows; y++) 01061 { 01062 (void) jpeg_read_scanlines(&jpeg_info,scanline,1); 01063 p=jpeg_pixels; 01064 q=SetImagePixels(image,0,y,image->columns,1); 01065 if (q == (PixelPacket *) NULL) 01066 break; 01067 indexes=GetIndexes(image); 01068 if (jpeg_info.data_precision > 8) 01069 { 01070 if (jpeg_info.output_components == 1) 01071 for (x=0; x < (long) image->columns; x++) 01072 { 01073 index=ConstrainColormapIndex(image,(unsigned long) 01074 (16*GETJSAMPLE(*p))); 01075 indexes[x]=(IndexPacket) index; 01076 *q++=image->colormap[index]; 01077 p++; 01078 } 01079 else 01080 for (x=0; x < (long) image->columns; x++) 01081 { 01082 q->red=ScaleShortToQuantum(16*GETJSAMPLE(*p++)); 01083 q->green=ScaleShortToQuantum(16*GETJSAMPLE(*p++)); 01084 q->blue=ScaleShortToQuantum(16*GETJSAMPLE(*p++)); 01085 if (image->colorspace == CMYKColorspace) 01086 indexes[x]=ScaleShortToQuantum(16*GETJSAMPLE(*p++)); 01087 q++; 01088 } 01089 } 01090 else 01091 if (jpeg_info.output_components == 1) 01092 for (x=0; x < (long) image->columns; x++) 01093 { 01094 index=ConstrainColormapIndex(image,(unsigned long) GETJSAMPLE(*p)); 01095 indexes[x]=(IndexPacket) index; 01096 *q++=image->colormap[index]; 01097 p++; 01098 } 01099 else 01100 for (x=0; x < (long) image->columns; x++) 01101 { 01102 q->red=ScaleCharToQuantum(GETJSAMPLE(*p++)); 01103 q->green=ScaleCharToQuantum(GETJSAMPLE(*p++)); 01104 q->blue=ScaleCharToQuantum(GETJSAMPLE(*p++)); 01105 if (image->colorspace == CMYKColorspace) 01106 indexes[x]=ScaleCharToQuantum(GETJSAMPLE(*p++)); 01107 q++; 01108 } 01109 if (SyncImagePixels(image) == MagickFalse) 01110 break; 01111 if ((image->progress_monitor != (MagickProgressMonitor) NULL) && 01112 (QuantumTick(y,image->rows) != MagickFalse)) 01113 { 01114 status=image->progress_monitor(LoadImageTag,y,image->rows, 01115 image->client_data); 01116 if (status == MagickFalse) 01117 break; 01118 } 01119 } 01120 if (jpeg_info.quantize_colors != MagickFalse) 01121 for (i=0; i < jpeg_info.actual_number_of_colors; i++) 01122 { 01123 image->colormap[i].red=ScaleCharToQuantum(jpeg_info.colormap[0][i]); 01124 image->colormap[i].green=image->colormap[i].red; 01125 image->colormap[i].blue=image->colormap[i].red; 01126 if (jpeg_info.out_color_space != JCS_GRAYSCALE) 01127 { 01128 image->colormap[i].green=ScaleCharToQuantum(jpeg_info.colormap[1][i]); 01129 image->colormap[i].blue=ScaleCharToQuantum(jpeg_info.colormap[2][i]); 01130 } 01131 } 01132 if (image->colorspace == CMYKColorspace) 01133 { 01134 /* 01135 Correct CMYK levels. 01136 */ 01137 for (y=0; y < (long) image->rows; y++) 01138 { 01139 q=GetImagePixels(image,0,y,image->columns,1); 01140 if (q == (PixelPacket *) NULL) 01141 break; 01142 indexes=GetIndexes(image); 01143 for (x=0; x < (long) image->columns; x++) 01144 { 01145 q->red=(Quantum) (MaxRGB-q->red); 01146 q->green=(Quantum) (MaxRGB-q->green); 01147 q->blue=(Quantum) (MaxRGB-q->blue); 01148 indexes[x]=(IndexPacket) (MaxRGB-indexes[x]); 01149 q++; 01150 } 01151 if (SyncImagePixels(image) == MagickFalse) 01152 break; 01153 } 01154 } 01155 /* 01156 Free jpeg resources. 01157 */ 01158 (void) jpeg_finish_decompress(&jpeg_info); 01159 jpeg_destroy_decompress(&jpeg_info); 01160 jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); 01161 CloseBlob(image); 01162 InheritException(exception,&image->exception); 01163 return(GetFirstImageInList(image)); 01164 } 01165 #endif 01166 01167 /* 01168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01169 % % 01170 % % 01171 % % 01172 % R e g i s t e r J P E G I m a g e % 01173 % % 01174 % % 01175 % % 01176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01177 % 01178 % RegisterJPEGImage() adds attributes for the JPEG image format to 01179 % the list of supported formats. The attributes include the image format 01180 % tag, a method to read and/or write the format, whether the format 01181 % supports the saving of more than one frame to the same file or blob, 01182 % whether the format supports native in-memory I/O, and a brief 01183 % description of the format. 01184 % 01185 % The format of the RegisterJPEGImage method is: 01186 % 01187 % RegisterJPEGImage(void) 01188 % 01189 */ 01190 ModuleExport void RegisterJPEGImage(void) 01191 { 01192 MagickInfo 01193 *entry; 01194 01195 entry=SetMagickInfo("JPEG"); 01196 entry->thread_support=MagickFalse; 01197 #if defined(HasJPEG) 01198 entry->decoder=(DecoderHandler *) ReadJPEGImage; 01199 entry->encoder=(EncoderHandler *) WriteJPEGImage; 01200 #endif 01201 entry->magick=(MagickHandler *) IsJPEG; 01202 entry->adjoin=MagickFalse; 01203 entry->description= 01204 AcquireString("Joint Photographic Experts Group JFIF format"); 01205 #if defined(JPEG_LIB_VERSION) 01206 { 01207 char 01208 version[MaxTextExtent]; 01209 01210 (void) FormatMagickString(version,MaxTextExtent,"%d",JPEG_LIB_VERSION); 01211 entry->version=AcquireString(version); 01212 } 01213 #endif 01214 entry->module=AcquireString("JPEG"); 01215 (void) RegisterMagickInfo(entry); 01216 entry=SetMagickInfo("JPG"); 01217 entry->thread_support=MagickFalse; 01218 #if defined(HasJPEG) 01219 entry->decoder=(DecoderHandler *) ReadJPEGImage; 01220 entry->encoder=(EncoderHandler *) WriteJPEGImage; 01221 #endif 01222 entry->adjoin=MagickFalse; 01223 entry->description= 01224 AcquireString("Joint Photographic Experts Group JFIF format"); 01225 entry->module=AcquireString("JPEG"); 01226 (void) RegisterMagickInfo(entry); 01227 entry=SetMagickInfo("PJPEG"); 01228 entry->thread_support=MagickFalse; 01229 #if defined(HasJPEG) 01230 entry->decoder=(DecoderHandler *) ReadJPEGImage; 01231 entry->encoder=(EncoderHandler *) WriteJPEGImage; 01232 #endif 01233 entry->adjoin=MagickFalse; 01234 entry->description= 01235 AcquireString("Progessive Joint Photographic Experts Group JFIF"); 01236 entry->module=AcquireString("JPEG"); 01237 (void) RegisterMagickInfo(entry); 01238 } 01239 01240 /* 01241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01242 % % 01243 % % 01244 % % 01245 % U n r e g i s t e r J P E G I m a g e % 01246 % % 01247 % % 01248 % % 01249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01250 % 01251 % UnregisterJPEGImage() removes format registrations made by the 01252 % JPEG module from the list of supported formats. 01253 % 01254 % The format of the UnregisterJPEGImage method is: 01255 % 01256 % UnregisterJPEGImage(void) 01257 % 01258 */ 01259 ModuleExport void UnregisterJPEGImage(void) 01260 { 01261 (void) UnregisterMagickInfo("JPEG"); 01262 (void) UnregisterMagickInfo("JPG"); 01263 } 01264 01265 #if defined(HasJPEG) 01266 /* 01267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01268 % % 01269 % % 01270 % % 01271 % W r i t e J P E G I m a g e % 01272 % % 01273 % % 01274 % % 01275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01276 % 01277 % WriteJPEGImage() writes a JPEG image file and returns it. It 01278 % allocates the memory necessary for the new Image structure and returns a 01279 % pointer to the new image. 01280 % 01281 % The format of the WriteJPEGImage method is: 01282 % 01283 % MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,Image *image) 01284 % 01285 % A description of each parameter follows: 01286 % 01287 % o image_info: The image info. 01288 % 01289 % o jpeg_image: The image. 01290 % 01291 % 01292 */ 01293 01294 static boolean EmptyOutputBuffer(j_compress_ptr cinfo) 01295 { 01296 DestinationManager 01297 *destination; 01298 01299 destination=(DestinationManager *) cinfo->dest; 01300 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image, 01301 MaxBufferExtent,destination->buffer); 01302 if (destination->manager.free_in_buffer != MaxBufferExtent) 01303 ERREXIT(cinfo,JERR_FILE_WRITE); 01304 destination->manager.next_output_byte=destination->buffer; 01305 return(TRUE); 01306 } 01307 01308 static void InitializeDestination(j_compress_ptr cinfo) 01309 { 01310 DestinationManager 01311 *destination; 01312 01313 destination=(DestinationManager *) cinfo->dest; 01314 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small) 01315 ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET)); 01316 destination->manager.next_output_byte=destination->buffer; 01317 destination->manager.free_in_buffer=MaxBufferExtent; 01318 } 01319 01320 static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level) 01321 { 01322 char 01323 message[JMSG_LENGTH_MAX]; 01324 01325 Image 01326 *image; 01327 01328 (jpeg_info->err->format_message)(jpeg_info,message); 01329 image=(Image *) jpeg_info->client_data; 01330 if (level < 0) 01331 { 01332 if ((jpeg_info->err->num_warnings == 0) || 01333 (jpeg_info->err->trace_level >= 3)) 01334 ThrowBinaryException(CorruptImageWarning,(char *) message, 01335 image->filename); 01336 jpeg_info->err->num_warnings++; 01337 } 01338 else 01339 if (jpeg_info->err->trace_level >= level) 01340 ThrowBinaryException(CoderError,(char *) message,image->filename); 01341 return(MagickTrue); 01342 } 01343 01344 static void TerminateDestination(j_compress_ptr cinfo) 01345 { 01346 DestinationManager 01347 *destination; 01348 01349 destination=(DestinationManager *) cinfo->dest; 01350 if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0) 01351 { 01352 ssize_t 01353 count; 01354 01355 count=WriteBlob(destination->image,MaxBufferExtent- 01356 destination->manager.free_in_buffer,destination->buffer); 01357 if (count != (ssize_t) 01358 (MaxBufferExtent-destination->manager.free_in_buffer)) 01359 ERREXIT(cinfo,JERR_FILE_WRITE); 01360 } 01361 if (SyncBlob(destination->image) != MagickFalse) 01362 ERREXIT(cinfo,JERR_FILE_WRITE); 01363 } 01364 01365 static void WriteProfile(j_compress_ptr jpeg_info,Image *image) 01366 { 01367 const char 01368 *name; 01369 01370 const StringInfo 01371 *profile; 01372 01373 register long 01374 i; 01375 01376 size_t 01377 length; 01378 01379 StringInfo 01380 *custom_profile; 01381 01382 unsigned long 01383 tag_length; 01384 01385 /* 01386 Save image profile as a APP marker. 01387 */ 01388 custom_profile=AcquireStringInfo(65535L); 01389 ResetImageProfileIterator(image); 01390 for (name=GetNextImageProfile(image); name != (const char *) NULL; ) 01391 { 01392 profile=GetImageProfile(image,name); 01393 if ((LocaleCompare(name,"EXIF") == 0) || (LocaleCompare(name,"XMP") == 0)) 01394 for (i=0; i < (long) profile->length; i+=65533L) 01395 { 01396 length=Min(profile->length-i,65533L); 01397 jpeg_write_marker(jpeg_info,XML_MARKER,profile->datum+i, 01398 (unsigned int) length); 01399 } 01400 if (LocaleCompare(name,"ICC") == 0) 01401 { 01402 tag_length=14; 01403 (void) CopyMagickMemory(custom_profile->datum,ICC_PROFILE,tag_length); 01404 for (i=0; i < (long) profile->length; i+=65519L) 01405 { 01406 length=Min(profile->length-i,65519L); 01407 custom_profile->datum[12]=(unsigned char) ((i/65519L)+1); 01408 custom_profile->datum[13]=(unsigned char) (profile->length/65519L+1); 01409 (void) CopyMagickMemory(custom_profile->datum+tag_length, 01410 profile->datum+i,length); 01411 jpeg_write_marker(jpeg_info,ICC_MARKER,custom_profile->datum, 01412 (unsigned int) (length+tag_length)); 01413 } 01414 } 01415 if (LocaleCompare(name,"IPTC") == 0) 01416 { 01417 unsigned long 01418 roundup; 01419 01420 tag_length=14; 01421 (void) CopyMagickMemory(custom_profile->datum,"Photoshop 3.0 ", 01422 tag_length); 01423 custom_profile->datum[13]=0x00; 01424 for (i=0; i < (long) profile->length; i+=65500L) 01425 { 01426 length=Min(profile->length-i,65500L); 01427 roundup=(unsigned long) (length & 0x01); 01428 (void) CopyMagickMemory(custom_profile->datum+tag_length, 01429 profile->datum+i,length); 01430 if (roundup != 0) 01431 custom_profile->datum[length+tag_length]='\0'; 01432 jpeg_write_marker(jpeg_info,IPTC_MARKER,custom_profile->datum, 01433 (unsigned int) (length+tag_length+roundup)); 01434 } 01435 } 01436 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 01437 "%s profile: %lu bytes",name,(unsigned long) profile->length); 01438 name=GetNextImageProfile(image); 01439 } 01440 custom_profile=DestroyStringInfo(custom_profile); 01441 } 01442 01443 static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image) 01444 { 01445 DestinationManager 01446 *destination; 01447 01448 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small) 01449 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager)); 01450 destination=(DestinationManager *) cinfo->dest; 01451 destination->manager.init_destination=InitializeDestination; 01452 destination->manager.empty_output_buffer=EmptyOutputBuffer; 01453 destination->manager.term_destination=TerminateDestination; 01454 destination->image=image; 01455 } 01456 01457 static char **SamplingFactorToList(const char *text) 01458 { 01459 char 01460 **textlist; 01461 01462 register char 01463 *q; 01464 01465 register const char 01466 *p; 01467 01468 register long 01469 i; 01470 01471 unsigned long 01472 lines; 01473 01474 if (text == (char *) NULL) 01475 return((char **) NULL); 01476 /* 01477 Convert string to an ASCII list. 01478 */ 01479 lines=1; 01480 for (p=text; *p != '\0'; p++) 01481 if (*p == ',') 01482 lines++; 01483 textlist=(char **) 01484 AcquireMagickMemory(((size_t) lines+MaxTextExtent)*sizeof(*textlist)); 01485 if (textlist == (char **) NULL) 01486 ThrowMagickFatalException(ResourceLimitFatalError,"UnableToConvertText", 01487 text); 01488 p=text; 01489 for (i=0; i < (long) lines; i++) 01490 { 01491 for (q=(char *) p; *q != '\0'; q++) 01492 if (*q == ',') 01493 break; 01494 textlist[i]=(char *) AcquireMagickMemory((size_t) (q-p)+MaxTextExtent); 01495 if (textlist[i] == (char *) NULL) 01496 ThrowMagickFatalException(ResourceLimitFatalError,"UnableToConvertText", 01497 text); 01498 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1)); 01499 if (*q == '\r') 01500 q++; 01501 p=q+1; 01502 } 01503 textlist[i]=(char *) NULL; 01504 return(textlist); 01505 } 01506 01507 static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info, 01508 Image *image) 01509 { 01510 char 01511 *sampling_factors; 01512 01513 const ImageAttribute 01514 *attribute; 01515 01516 JSAMPLE 01517 *jpeg_pixels; 01518 01519 JSAMPROW 01520 scanline[1]; 01521 01522 long 01523 y; 01524 01525 MagickBooleanType 01526 status; 01527 01528 MagickSizeType 01529 number_pixels; 01530 01531 register const PixelPacket 01532 *p; 01533 01534 register IndexPacket 01535 *indexes; 01536 01537 register JSAMPLE 01538 *q; 01539 01540 register long 01541 i, 01542 x; 01543 01544 struct jpeg_compress_struct 01545 jpeg_info; 01546 01547 struct jpeg_error_mgr 01548 jpeg_error; 01549 01550 /* 01551 Open image file. 01552 */ 01553 assert(image_info != (const ImageInfo *) NULL); 01554 assert(image_info->signature == MagickSignature); 01555 assert(image != (Image *) NULL); 01556 assert(image->signature == MagickSignature); 01557 if (image->debug != MagickFalse) 01558 (void) LogMagickEvent(TraceEvent,GetMagickModule(),image->filename); 01559 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); 01560 if (status == MagickFalse) 01561 return(status); 01562 /* 01563 Initialize JPEG parameters. 01564 */ 01565 jpeg_info.client_data=(void *) image; 01566 jpeg_info.err=jpeg_std_error(&jpeg_error); 01567 jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler; 01568 jpeg_create_compress(&jpeg_info); 01569 JPEGDestinationManager(&jpeg_info,image); 01570 jpeg_info.image_width=(unsigned int) image->columns; 01571 jpeg_info.image_height=(unsigned int) image->rows; 01572 jpeg_info.input_components=3; 01573 jpeg_info.data_precision=(int) Min(image->depth,BITS_IN_JSAMPLE); 01574 jpeg_info.in_color_space=JCS_RGB; 01575 switch (image_info->colorspace) 01576 { 01577 case CMYKColorspace: 01578 { 01579 jpeg_info.input_components=4; 01580 jpeg_info.in_color_space=JCS_CMYK; 01581 (void) SetImageColorspace(image,CMYKColorspace); 01582 break; 01583 } 01584 case YCbCrColorspace: 01585 { 01586 jpeg_info.in_color_space=JCS_YCbCr; 01587 (void) SetImageColorspace(image,YCbCrColorspace); 01588 break; 01589 } 01590 case GRAYColorspace: 01591 { 01592 jpeg_info.in_color_space=JCS_GRAYSCALE; 01593 SetImageColorspace(image,GRAYColorspace); 01594 break; 01595 } 01596 default: 01597 { 01598 if (image->colorspace == CMYKColorspace) 01599 { 01600 jpeg_info.input_components=4; 01601 jpeg_info.in_color_space=JCS_CMYK; 01602 break; 01603 } 01604 if (image->colorspace == YCbCrColorspace) 01605 { 01606 jpeg_info.in_color_space=JCS_YCbCr; 01607 break; 01608 } 01609 (void) SetImageColorspace(image,RGBColorspace); 01610 break; 01611 } 01612 } 01613 if ((image_info->type != TrueColorType) && 01614 (IsGrayImage(image,&image->exception) != MagickFalse)) 01615 { 01616 jpeg_info.input_components=1; 01617 jpeg_info.in_color_space=JCS_GRAYSCALE; 01618 } 01619 jpeg_set_defaults(&jpeg_info); 01620 jpeg_info.density_unit=(UINT8) 1; 01621 if (image->debug != MagickFalse) 01622 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 01623 "Image resolution: %ld,%ld",(long) (image->x_resolution+0.5), 01624 (long) (image->y_resolution+0.5)); 01625 if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0)) 01626 { 01627 /* 01628 Set image resolution. 01629 */ 01630 jpeg_info.write_JFIF_header=MagickTrue; 01631 jpeg_info.X_density=(UINT16) image->x_resolution; 01632 jpeg_info.Y_density=(UINT16) image->y_resolution; 01633 if (image->units == PixelsPerInchResolution) 01634 jpeg_info.density_unit=(UINT8) 1; 01635 if (image->units == PixelsPerCentimeterResolution) 01636 jpeg_info.density_unit=(UINT8) 2; 01637 } 01638 number_pixels=(MagickSizeType) image->columns*image->rows; 01639 if (number_pixels == (MagickSizeType) ((size_t) number_pixels)) 01640 { 01641 /* 01642 Perform optimization only if available memory resources permit it. 01643 */ 01644 status=AcquireMagickResource(AreaResource,number_pixels); 01645 if (status != MagickFalse) 01646 jpeg_info.optimize_coding=MagickTrue; 01647 RelinquishMagickResource(AreaResource,number_pixels); 01648 } 01649 #if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED) 01650 if