/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % AAA V V IIIII % % A A V V I % % AAAAA V V I % % A A V V I % % A A V IIIII % % % % % % Read Microsoft Audio/Visual Interleaved Image Format. % % % % Software Design % % John Cristy % % Nathan Brown % % March 2004 % % % % % % Copyright 1999-2007 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % % http://www.imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % */ /* Include declarations. */ #include #include "magick/studio.h" #include "magick/blob.h" #include "magick/blob-private.h" #include "magick/color-private.h" #include "magick/color-private.h" #include "magick/colorspace.h" #include "magick/constitute.h" #include "magick/exception.h" #include "magick/exception-private.h" #include "magick/geometry.h" #include "magick/image.h" #include "magick/image-private.h" #include "magick/list.h" #include "magick/log.h" #include "magick/magick.h" #include "magick/memory_.h" #include "magick/monitor.h" #include "magick/profile.h" #include "magick/property.h" #include "magick/quantum-private.h" #include "magick/static.h" #include "magick/string_.h" #include "magick/module.h" #include "magick/utility.h" #if defined(HasJPEG) #define JPEG_INTERNAL_OPTIONS #if defined(__MINGW32__) # define XMD_H 1 /* Avoid conflicting typedef for INT32 */ #endif #undef HAVE_STDLIB_H #include "jpeglib.h" #include "jerror.h" #endif /* Define declarations. */ #define ICC_MARKER (JPEG_APP0+2) #define ICC_PROFILE "ICC_PROFILE" #define IPTC_MARKER (JPEG_APP0+13) #define XML_MARKER (JPEG_APP0+1) #define MaxBufferExtent 8192 /* Typedef declarations. */ #if defined(HasJPEG) typedef struct _ErrorManager { Image *image; jmp_buf error_recovery; boolean verbose; } ErrorManager; typedef struct _SourceManager { struct jpeg_source_mgr manager; Image *image; unsigned char *source_data; size_t source_length; JOCTET *buffer; boolean start_of_blob; } SourceManager; #endif /* Typedef declaractions. */ typedef struct _AVIInfo { unsigned long delay, max_data_rate, pad_granularity, flags, total_frames, initial_frames, number_streams, buffer_size, width, height, time_scale, data_rate, start_time, data_length; } AVIInfo; typedef struct _BMPInfo { unsigned long size, width, height, planes, bits_per_pixel; char compression[5]; unsigned long image_size, x_pixels, y_pixels, number_colors, important_colors; } BMPInfo; typedef struct _AVIStreamInfo { char data_type[5], data_handler[5]; unsigned long flags, priority, initial_frames, time_scale, data_rate, start_time, data_length, buffer_size, quality, sample_size; } AVIStreamInfo; /* MJPEG Information from Microsoft SDK */ #if defined(HasJPEG) #ifndef NOJPEGDIB /* New DIB Compression Defines */ #define JPEG_DIB mmioFOURCC('J','P','E','G') /* Still image JPEG DIB biCompression */ #define MJPG_DIB mmioFOURCC('M','J','P','G') /* Motion JPEG DIB biCompression */ /* JPEGColorSpaceID Definitions */ #define JPEG_Y 1 /* Y only component of YCbCr */ #define JPEG_YCbCr 2 /* YCbCr as define by CCIR 601 */ #define JPEG_RGB 3 /* 3 component RGB */ /* Structure definitions */ #if defined(MAGICK_FUTURE) typedef struct tagJPEGINFOHEADER { /* compression-specific fields */ /* these fields are defined for 'JPEG' and 'MJPG' */ DWORD JPEGSize; DWORD JPEGProcess; /* Process specific fields */ DWORD JPEGColorSpaceID; DWORD JPEGBitsPerSample; DWORD JPEGHSubSampling; DWORD JPEGVSubSampling; } JPEGINFOHEADER; #endif /* Default DHT Segment */ static const unsigned char MJPGDHTSeg[0x1A8] = { /* JPEG DHT Segment for YCrCb omitted from MJPG data */ 0xFF,0xD8,0xFF,0xC4,0x01,0xA2, 0x00,0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x01,0x00,0x03,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x10,0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,0x05,0x04,0x04,0x00, 0x00,0x01,0x7D,0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61, 0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xA1,0x08,0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1,0xF0,0x24, 0x33,0x62,0x72,0x82,0x09,0x0A,0x16,0x17,0x18,0x19,0x1A,0x25,0x26,0x27,0x28,0x29,0x2A,0x34, 0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x53,0x54,0x55,0x56, 0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7A,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99, 0x9A,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9, 0xBA,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9, 0xDA,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, 0xF8,0xF9,0xFA,0x11,0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01, 0x02,0x77,0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xA1,0xB1,0xC1,0x09,0x23,0x33,0x52,0xF0,0x15,0x62, 0x72,0xD1,0x0A,0x16,0x24,0x34,0xE1,0x25,0xF1,0x17,0x18,0x19,0x1A,0x26,0x27,0x28,0x29,0x2A, 0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x53,0x54,0x55,0x56, 0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7A,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 0x99,0x9A,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8, 0xB9,0xBA,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8, 0xD9,0xDA,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8, 0xF9,0xFA,0xFF,0xD9 }; /* End DHT default */ /* End JPEG */ #endif #endif static inline size_t MagickMax(const size_t x,const size_t y) { if (x > y) return(x); return(y); } static inline size_t MagickMin(const size_t x,const size_t y) { if (x < y) return(x); return(y); } #if defined(HasJPEG) /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d J P E G I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ReadJPEGImage() reads a JPEG image file and returns it. It allocates % the memory necessary for the new Image structure and returns a pointer to % the new image. % % The format of the ReadJPEGImage method is: % % Image *ReadJPEGImage(const ImageInfo *image_info, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image_info: The image info. % % o exception: return any errors or warnings in this structure. % */ static MagickBooleanType EmitMessage(j_common_ptr jpeg_info,int level) { char message[JMSG_LENGTH_MAX]; ErrorManager *error_manager; Image *image; (jpeg_info->err->format_message)(jpeg_info,message); error_manager=(ErrorManager *) jpeg_info->client_data; image=error_manager->image; if (error_manager->verbose != MagickFalse) (void) fprintf(stdout,"%s\n",message); if (level < 0) { if ((jpeg_info->err->num_warnings == 0) || (jpeg_info->err->trace_level >= 3)) ThrowBinaryException(CorruptImageError,(char *) message, image->filename); jpeg_info->err->num_warnings++; } else if (jpeg_info->err->trace_level >= level) ThrowBinaryException(CoderError,(char *) message,image->filename); return(MagickTrue); } static boolean FillInputBuffer(j_decompress_ptr cinfo) { SourceManager *source; source=(SourceManager *) cinfo->src; if (source->image) source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image, MagickMin(source->source_length,MaxBufferExtent),source->buffer); else { source->manager.bytes_in_buffer=(size_t) MagickMin(source->source_length, MaxBufferExtent); (void) CopyMagickMemory(source->buffer,source->source_data, source->manager.bytes_in_buffer); source->source_data+=source->manager.bytes_in_buffer; } source->source_length-=source->manager.bytes_in_buffer; if (source->manager.bytes_in_buffer == 0) { if (source->start_of_blob != 0) ERREXIT(cinfo,JERR_INPUT_EMPTY); WARNMS(cinfo,JWRN_JPEG_EOF); source->buffer[0]=(JOCTET) 0xff; source->buffer[1]=(JOCTET) JPEG_EOI; source->manager.bytes_in_buffer=2; } source->manager.next_input_byte=source->buffer; source->start_of_blob=FALSE; return(TRUE); } static int GetCharacter(j_decompress_ptr jpeg_info) { if (jpeg_info->src->bytes_in_buffer == 0) (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info); jpeg_info->src->bytes_in_buffer--; return((int) GETJOCTET(*jpeg_info->src->next_input_byte++)); } static void InitializeSource(j_decompress_ptr cinfo) { SourceManager *source; source=(SourceManager *) cinfo->src; source->start_of_blob=TRUE; } static void JPEGErrorHandler(j_common_ptr jpeg_info) { ErrorManager *error_manager; (void) EmitMessage(jpeg_info,0); error_manager=(ErrorManager *) jpeg_info->client_data; longjmp(error_manager->error_recovery,1); } static boolean ReadComment(j_decompress_ptr jpeg_info) { char *comment; ErrorManager *error_manager; Image *image; register char *p; register long i; size_t length; /* Determine length of comment. */ error_manager=(ErrorManager *) jpeg_info->client_data; image=error_manager->image; length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); length+=GetCharacter(jpeg_info); length-=2; if (length <= 0) return(MagickTrue); comment=(char *) NULL; if (~length >= MaxTextExtent) comment=(char *) AcquireQuantumMemory(length+MaxTextExtent, sizeof(*comment)); if (comment == (char *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); /* Read comment. */ i=(long) length-1; for (p=comment; i-- >= 0; p++) *p=(char) GetCharacter(jpeg_info); *p='\0'; (void) SetImageProperty(image,"comment",comment); comment=DestroyString(comment); return(MagickTrue); } static boolean ReadICCProfile(j_decompress_ptr jpeg_info) { char magick[12]; ErrorManager *error_manager; Image *image; MagickBooleanType status; register long i; register unsigned char *p; size_t length; StringInfo *icc_profile, *profile; /* Read color profile. */ length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); length+=(size_t) GetCharacter(jpeg_info); if (length < 2) return(MagickFalse); length-=2; if (length <= 14) { while (length-- != 0) (void) GetCharacter(jpeg_info); return(MagickTrue); } for (i=0; i < 12; i++) magick[i]=(char) GetCharacter(jpeg_info); if (LocaleCompare(magick,ICC_PROFILE) != 0) { /* Not a ICC profile, return. */ for (i=0; i < (long) (length-12); i++) (void) GetCharacter(jpeg_info); return(MagickTrue); } (void) GetCharacter(jpeg_info); /* id */ (void) GetCharacter(jpeg_info); /* markers */ if (length < 14) return(MagickFalse); length-=14; error_manager=(ErrorManager *) jpeg_info->client_data; image=error_manager->image; profile=AcquireStringInfo(length); if (profile == (StringInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); p=GetStringInfoDatum(profile); for (i=(long) GetStringInfoLength(profile)-1; i >= 0; i--) *p++=(unsigned char) GetCharacter(jpeg_info); icc_profile=(StringInfo *) GetImageProfile(image,"icc"); if (icc_profile != (StringInfo *) NULL) ConcatenateStringInfo(icc_profile,profile); else { status=SetImageProfile(image,"icc",profile); if (status == MagickFalse) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); } if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Profile: ICC, %lu bytes",(unsigned long) length); return(MagickTrue); } static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info) { char magick[MaxTextExtent]; ErrorManager *error_manager; Image *image; MagickBooleanType status; register long i; register unsigned char *p; size_t length, tag_length; StringInfo *iptc_profile, *profile; #ifdef GET_ONLY_IPTC_DATA unsigned char tag[MaxTextExtent]; #endif /* Determine length of binary data stored here. */ length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); length+=(size_t) GetCharacter(jpeg_info); if (length <= 2) return(MagickTrue); length-=2; tag_length=0; #ifdef GET_ONLY_IPTC_DATA *tag='\0'; #endif error_manager=(ErrorManager *) jpeg_info->client_data; image=error_manager->image; iptc_profile=(StringInfo *) GetImageProfile(image,"iptc"); if (iptc_profile == (StringInfo *) NULL) { #ifdef GET_ONLY_IPTC_DATA /* Find the beginning of the iptc portion of the binary data. */ for (*tag='\0'; length > 0; ) { *tag=GetCharacter(jpeg_info); *(tag+1)=GetCharacter(jpeg_info); if (length < 2) return(MagickFalse); length-=2; if ((*tag == 0x1c) && (*(tag+1) == 0x02)) break; } tag_length=2; #else /* Validate that this was written as a Photoshop resource format slug. */ for (i=0; i < 10; i++) magick[i]=(char) GetCharacter(jpeg_info); magick[10]='\0'; if (length <= 10) return(MagickTrue); length-=10; if (LocaleCompare(magick,"Photoshop ") != 0) { /* Not a ICC profile, return. */ for (i=0; i < (long) length; i++) (void) GetCharacter(jpeg_info); return(MagickTrue); } /* Remove the version number. */ for (i=0; i < 4; i++) (void) GetCharacter(jpeg_info); if (length <= 4) return(MagickTrue); length-=4; tag_length=0; #endif } if (length == 0) return(MagickTrue); profile=AcquireStringInfo(length); if (profile == (StringInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); p=GetStringInfoDatum(profile); for (i=(long) GetStringInfoLength(profile)-1; i >= 0; i--) *p++=(unsigned char) GetCharacter(jpeg_info); iptc_profile=(StringInfo *) GetImageProfile(image,"iptc"); if (iptc_profile != (StringInfo *) NULL) ConcatenateStringInfo(iptc_profile,profile); else { status=SetImageProfile(image,"iptc",profile); if (status == MagickFalse) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); } if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Profile: iptc, %lu bytes",(unsigned long) length); return(MagickTrue); } static boolean ReadProfile(j_decompress_ptr jpeg_info) { char name[MaxTextExtent]; ErrorManager *error_manager; Image *image; int marker; MagickBooleanType status; register long i; register unsigned char *p; size_t length; StringInfo *profile; /* Read generic profile. */ length=(size_t) ((unsigned long) GetCharacter(jpeg_info) << 8); length+=(size_t) GetCharacter(jpeg_info); if (length <= 2) return(MagickTrue); length-=2; marker=jpeg_info->unread_marker-JPEG_APP0; error_manager=(ErrorManager *) jpeg_info->client_data; image=error_manager->image; (void) FormatMagickString(name,MaxTextExtent,"APP%d",marker); profile=AcquireStringInfo(length); if (profile == (StringInfo *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); p=GetStringInfoDatum(profile); for (i=(long) GetStringInfoLength(profile)-1; i >= 0; i--) *p++=(unsigned char) GetCharacter(jpeg_info); if (marker == 1) { p=GetStringInfoDatum(profile); if ((length > 4) && (LocaleNCompare((char *) p,"exif",4) == 0)) (void) CopyMagickString(name,"iptc",MaxTextExtent); if ((length > 5) && (LocaleNCompare((char *) p,"http:",5) == 0)) (void) CopyMagickString(name,"xmp",MaxTextExtent); } status=SetImageProfile(image,name,profile); if (status == MagickFalse) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Profile: %s, %lu bytes",name,(unsigned long) length); return(MagickTrue); } static void SkipInputData(j_decompress_ptr cinfo,long number_bytes) { SourceManager *source; if (number_bytes <= 0) return; source=(SourceManager *) cinfo->src; while (number_bytes > (long) source->manager.bytes_in_buffer) { number_bytes-=(long) source->manager.bytes_in_buffer; (void) FillInputBuffer(cinfo); } source->manager.next_input_byte+=(size_t) number_bytes; source->manager.bytes_in_buffer-=(size_t) number_bytes; } static void TerminateSource(j_decompress_ptr cinfo) { cinfo=cinfo; } static void JPEGSourceManagerMemory(j_decompress_ptr cinfo,unsigned char *source_data, unsigned long source_length) { SourceManager *source; cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager)); source=(SourceManager *) cinfo->src; source->buffer=(JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET)); source=(SourceManager *) cinfo->src; source->manager.init_source=InitializeSource; source->manager.fill_input_buffer=FillInputBuffer; source->manager.skip_input_data=SkipInputData; source->manager.resync_to_restart=jpeg_resync_to_restart; source->manager.term_source=TerminateSource; source->manager.bytes_in_buffer=0; source->manager.next_input_byte=NULL; source->image=0; source->source_data=source_data; source->source_length=source_length; } static void JPEGSourceManagerBLOB(j_decompress_ptr cinfo,Image *image, unsigned long source_length) { SourceManager *source; cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager)); source=(SourceManager *) cinfo->src; source->buffer=(JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET)); source=(SourceManager *) cinfo->src; source->manager.init_source=InitializeSource; source->manager.fill_input_buffer=FillInputBuffer; source->manager.skip_input_data=SkipInputData; source->manager.resync_to_restart=jpeg_resync_to_restart; source->manager.term_source=TerminateSource; source->manager.bytes_in_buffer=0; source->manager.next_input_byte=NULL; source->image=image; source->source_data=0; source->source_length=source_length; } #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D e c o d e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % DecodeImage unpacks the packed image pixels into runlength-encoded % pixel packets. % % The format of the DecodeImage method is: % % MagickBooleanType DecodeImage(Image *image, % const MagickBooleanType compression,unsigned char *pixels) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o compression: A value of 1 means the compressed pixels are runlength % encoded for a 256-color bitmap. A value of 2 means a 16-color bitmap. % % o pixels: The address of a byte (8 bits) array of pixel data created by % the decoding process. % */ static MagickBooleanType DecodeImage(Image *image, const MagickBooleanType compression,unsigned char *pixels) { #if !defined(__WINDOWS__) || defined(__MINGW32__) #define BI_RLE8 1 #endif long y; MagickBooleanType status; register long i, x; register unsigned char *p, *q; ssize_t count; unsigned char byte; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(pixels != (unsigned char *) NULL); (void) ResetMagickMemory(pixels,0,(size_t) image->columns*image->rows* sizeof(*pixels)); byte=0; x=0; p=pixels; q=pixels+(size_t) image->columns*image->rows; for (y=0; y < (long) image->rows; ) { if ((p < pixels) || (p >= q)) break; count=(ssize_t) ReadBlobByte(image); if ((int) count == EOF) break; if (count != 0) { count=(ssize_t) MagickMin((size_t) count,(size_t) (q-p)); /* Encoded mode. */ byte=(unsigned char) ReadBlobByte(image); if (compression == BI_RLE8) { for (i=0; i < (long) count; i++) *p++=(unsigned char) byte; } else { for (i=0; i < (long) count; i++) *p++=(unsigned char) ((i & 0x01) != 0 ? (byte & 0x0f) : ((byte >> 4) & 0x0f)); } x+=count; } else { /* Escape mode. */ count=(ssize_t) ReadBlobByte(image); if (count == 0x01) return(MagickTrue); switch (count) { case 0x00: { /* End of line. */ x=0; y++; p=pixels+y*image->columns; break; } case 0x02: { /* Delta mode. */ x+=ReadBlobByte(image); y+=ReadBlobByte(image); p=pixels+y*image->columns+x; break; } default: { /* Absolute mode. */ count=(ssize_t) MagickMin((size_t) count,(size_t) (q-p)); if (compression == BI_RLE8) for (i=0; i < (long) count; i++) *p++=(unsigned char) ReadBlobByte(image); else for (i=0; i < (long) count; i++) { if ((i & 0x01) == 0) byte=(unsigned char) ReadBlobByte(image); *p++=(unsigned char) ((i & 0x01) != 0 ? (byte & 0x0f) : ((byte >> 4) & 0x0f)); } x+=count; /* Read pad byte. */ if (compression == BI_RLE8) { if ((count & 0x01) != 0) (void) ReadBlobByte(image); } else if (((count & 0x03) == 1) || ((count & 0x03) == 2)) (void) ReadBlobByte(image); break; } } } if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(y,image->rows) != MagickFalse)) { status=image->progress_monitor(LoadImageTag,y,image->rows, image->client_data); if (status == MagickFalse) break; } } (void) ReadBlobByte(image); /* end of line */ (void) ReadBlobByte(image); return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s A V I % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsAVI() returns MagickTrue if the image format type, identified by the % magick string, is Audio/Video Interleaved file format. % % The format of the IsAVI method is: % % unsigned long IsAVI(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % % o magick: This string is generally the first few bytes of an image file % or blob. % % o length: Specifies the length of the magick string. % */ static MagickBooleanType IsAVI(const unsigned char *magick,const size_t length) { if (length < 4) return(MagickFalse); if (memcmp(magick,"RIFF",4) == 0) return(MagickTrue); return(MagickFalse); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d A V I I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ReadAVIImage() reads an Audio Video Interleave image file and returns % it. It allocates the memory necessary for the new Image structure and % returns a pointer to the new image. % % The format of the ReadAVIImage method is: % % Image *ReadAVIImage(const ImageInfo *image_info,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: Method ReadAVIImage returns a pointer to the image after % reading. A null image is returned if there is a memory shortage or if % the image cannot be read. % % o image_info: The image info. % % o exception: return any errors or warnings in this structure. % */ static Image *ReadAVIImage(const ImageInfo *image_info,ExceptionInfo *exception) { AVIInfo avi_info; AVIStreamInfo stream_info; BMPInfo bmp_info; char id[MaxTextExtent], message[MaxTextExtent]; Image *image; IndexPacket index; long y; MagickBooleanType status; MagickOffsetType offset; PixelPacket *colormap; register IndexPacket *indexes; register long x; register PixelPacket *q; register long i; register unsigned char *p; ssize_t count; unsigned char *pixels; unsigned long bit, bytes_per_line, chunk_size, number_colors; #if defined(HasJPEG) const char *value; ErrorManager error_manager; GeometryInfo geometry_info; JSAMPLE *jpeg_pixels; JSAMPROW scanline[1]; struct jpeg_decompress_struct jpeg_info; struct jpeg_error_mgr jpeg_error; unsigned long number_pixels, units; #endif /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickSignature); if (image_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", image_info->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickSignature); image=AllocateImage(image_info); status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == MagickFalse) { image=DestroyImageList(image); return((Image *) NULL); } (void) ResetMagickMemory(&avi_info,0,sizeof(AVIInfo)); (void) ResetMagickMemory(&bmp_info,0,sizeof(BMPInfo)); colormap=(PixelPacket *) NULL; number_colors=0; #if defined(HasJPEG) /* Initialize JPEG image structure. */ jpeg_info.err=jpeg_std_error(&jpeg_error); jpeg_info.err->emit_message=(void (*)(j_common_ptr,int)) EmitMessage; jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler; jpeg_pixels=(JSAMPLE *) NULL; error_manager.image=image; error_manager.verbose=(int) image_info->verbose; if (setjmp(error_manager.error_recovery) != 0) { if (jpeg_pixels != (JSAMPLE *) NULL) jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); jpeg_destroy_decompress(&jpeg_info); InheritException(exception,&image->exception); number_pixels=image->columns*image->rows; if (number_pixels != 0) return(GetFirstImageInList(image)); image=DestroyImage(image); return((Image *) NULL); } jpeg_info.client_data=(void *) &error_manager; jpeg_create_decompress(&jpeg_info); JPEGSourceManagerMemory(&jpeg_info, (unsigned char *) &MJPGDHTSeg, 0x1A8); jpeg_read_header(&jpeg_info, FALSE); #endif for ( ; ; ) { count=ReadBlob(image,4,(unsigned char *) id); if (count != 4) break; id[4]='\0'; chunk_size=ReadBlobLSBLong(image); if (chunk_size == 0) break; if ((chunk_size & 0x01) != 0) chunk_size++; if ((GetBlobSize(image) != 0) && ((MagickSizeType) chunk_size > GetBlobSize(image))) ThrowReaderException(CorruptImageError,"NotEnoughPixelData"); if (image_info->verbose != MagickFalse) { (void) fprintf(stdout,"AVI cid %s\n",id); (void) fprintf(stdout," chunk size %lu\n",chunk_size); } if ((LocaleCompare(id,"00db") == 0) || (LocaleCompare(id,"00dc") == 0)) { /* Read JPG compressed image, uses only YCbCr color space encoding and does not include the JPEG Huffman table, as it is pre-defined. */ if (LocaleCompare(bmp_info.compression,"MJPG") == 0) { #if defined(HasJPEG) JPEGSourceManagerBLOB(&jpeg_info,image,chunk_size); jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment); jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile); jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile); for (i=1; i < 16; i++) if ((i != 2) && (i != 13) && (i != 14)) jpeg_set_marker_processor(&jpeg_info,(int) i+JPEG_APP0,ReadProfile); i=jpeg_read_header(&jpeg_info,MagickTrue); if (jpeg_info.out_color_space == JCS_CMYK) image->colorspace=CMYKColorspace; /* Set image resolution. */ units=0; if ((jpeg_info.saw_JFIF_marker != 0) && (jpeg_info.X_density != 1) && (jpeg_info.Y_density != 1)) { image->x_resolution=(double) jpeg_info.X_density; image->y_resolution=(double) jpeg_info.Y_density; units=(unsigned long) jpeg_info.density_unit; } value=GetImageProperty(image,"exif:XResolution"); if (value != (char *) NULL) { (void) ParseGeometry(value,&geometry_info); if (geometry_info.sigma != 0) image->x_resolution=geometry_info.rho/geometry_info.sigma; (void) SetImageProperty(image,"exif:XResolution",(char *) NULL); } value=GetImageProperty(image,"exif:YResolution"); if (value != (char *) NULL) { (void) ParseGeometry(value,&geometry_info); if (geometry_info.sigma != 0) image->y_resolution=geometry_info.rho/geometry_info.sigma; (void) SetImageProperty(image,"exif:YResolution",(char *) NULL); } if (units == 1) image->units=PixelsPerInchResolution; if (units == 2) image->units=PixelsPerCentimeterResolution; number_pixels=image->columns*image->rows; if (number_pixels != 0) { double scale_factor; /* Let the JPEG library subsample for us. */ jpeg_calc_output_dimensions(&jpeg_info); image->magick_columns=jpeg_info.output_width; image->magick_rows=jpeg_info.output_height; scale_factor=(double) jpeg_info.output_width/image->columns; if (scale_factor > ((double) jpeg_info.output_height/image->rows)) scale_factor=(double) jpeg_info.output_height/image->rows; jpeg_info.scale_denom=(unsigned int) scale_factor; jpeg_calc_output_dimensions(&jpeg_info); if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Scale factor: %ld",(long) scale_factor); } if (image_info->subrange != 0) { jpeg_info.scale_denom=(unsigned int) image_info->subrange; jpeg_calc_output_dimensions(&jpeg_info); } #if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED) #ifdef D_LOSSLESS_SUPPORTED image->interlace=jpeg_info.process == JPROC_PROGRESSIVE ? PlaneInterlace : NoInterlace; image->compression=jpeg_info.process == JPROC_LOSSLESS ? LosslessJPEGCompression : JPEGCompression; if (jpeg_info.data_precision > 8) (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning, "12-bit JPEG not supported. Reducing pixel data to 8 bits", "`%s'",image->filename); #else image->interlace=jpeg_info.progressive_mode != 0 ? PlaneInterlace : NoInterlace; image->compression=JPEGCompression; #endif #else image->compression=JPEGCompression; image->interlace=PlaneInterlace; #endif if ((image_info->colors != 0) && (image_info->colors <= 256)) { /* Let the JPEG library quantize for us. */ jpeg_info.quantize_colors=MagickTrue; jpeg_info.desired_number_of_colors=(int) image_info->colors; if (AllocateImageColormap(image,image_info->colors) == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } (void) jpeg_start_decompress(&jpeg_info); image->columns=jpeg_info.output_width; image->rows=jpeg_info.output_height; image->depth=(unsigned long) jpeg_info.data_precision; if ((jpeg_info.output_components == 1) && (jpeg_info.quantize_colors == MagickFalse)) { if (AllocateImageColormap(image,1UL << image->depth) == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); } if (image->debug != MagickFalse) { if (image->interlace == PlaneInterlace) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Interlace: progressive"); else (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Interlace: nonprogressive"); (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Data precision: %d",(int) jpeg_info.data_precision); (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Geometry: %dx%d",(int) jpeg_info.output_width,(int) jpeg_info.output_height); #ifdef D_LOSSLESS_SUPPORTED if (image->compression==LosslessJPEGCompression) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Quality: 100 (lossless)"); else #endif { long hashval, sum; /* Log the JPEG quality that was used for compression. */ sum=0; for (i=0; i < NUM_QUANT_TBLS; i++) { int j; if (jpeg_info.quant_tbl_ptrs[i] != NULL) for (j=0; j < DCTSIZE2; j++) { UINT16 *c; c=jpeg_info.quant_tbl_ptrs[i]->quantval; sum+=c[j]; } } if ((jpeg_info.quant_tbl_ptrs[0] != NULL) && (jpeg_info.quant_tbl_ptrs[1] != NULL)) { int hash[]= { 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645, 632, 623, 613, 607, 600, 594, 589, 585, 581, 571, 555, 542, 529, 514, 494, 474, 457, 439, 424, 410, 397, 386, 373, 364, 351, 341, 334, 324, 317, 309, 299, 294, 287, 279, 274, 267, 262, 257, 251, 247, 243, 237, 232, 227, 222, 217, 213, 207, 202, 198, 192, 188, 183, 177, 173, 168, 163, 157, 153, 148, 143, 139, 132, 128, 125, 119, 115, 108, 104, 99, 94, 90, 84, 79, 74, 70, 64, 59, 55, 49, 45, 40, 34, 30, 25, 20, 15, 11, 6, 4, 0 }, sums[]= { 32640,32635,32266,31495,30665,29804,29146,28599,28104,27670, 27225,26725,26210,25716,25240,24789,24373,23946,23572,22846, 21801,20842,19949,19121,18386,17651,16998,16349,15800,15247, 14783,14321,13859,13535,13081,12702,12423,12056,11779,11513, 11135,10955,10676,10392,10208, 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458, 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788, 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128, 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509, 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846, 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201, 128, 0 }; hashval=(long) ((jpeg_info.quant_tbl_ptrs[0]->quantval[2]+ jpeg_info.quant_tbl_ptrs[0]->quantval[53]+ jpeg_info.quant_tbl_ptrs[1]->quantval[0]+ jpeg_info.quant_tbl_ptrs[1]->quantval[DCTSIZE2-1])); for (i=0; i < 100; i++) { if ((hashval >= hash[i]) || (sum >= sums[i])) { if ((hashval > hash[i]) || (sum > sums[i])) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Quality: %ld (approximate)",i+1); else (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Quality: %ld",i+1); break; } } } else if (jpeg_info.quant_tbl_ptrs[0] != NULL) { int bwhash[]= { 510, 505, 422, 380, 355, 338, 326, 318, 311, 305, 300, 297, 293, 291, 288, 286, 284, 283, 281, 280, 279, 278, 277, 273, 262, 251, 243, 233, 225, 218, 211, 205, 198, 193, 186, 181, 177, 172, 168, 164, 158, 156, 152, 148, 145, 142, 139, 136, 133, 131, 129, 126, 123, 120, 118, 115, 113, 110, 107, 105, 102, 100, 97, 94, 92, 89, 87, 83, 81, 79, 76, 74, 70, 68, 66, 63, 61, 57, 55, 52, 50, 48, 44, 42, 39, 37, 34, 31, 29, 26, 24, 21, 18, 16, 13, 11, 8, 6, 3, 2, 0 }, bwsum[]= { 16320,16315,15946,15277,14655,14073,13623,13230,12859,12560, 12240,11861,11456,11081,10714,10360,10027, 9679, 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823, 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086, 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092, 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396, 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727, 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068, 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398, 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736, 667, 592, 518, 441, 369, 292, 221, 151, 86, 64, 0 }; hashval=(long) ((jpeg_info.quant_tbl_ptrs[0]->quantval[2]+ jpeg_info.quant_tbl_ptrs[0]->quantval[53])); for (i=0; i < 100; i++) { if ((hashval >= bwhash[i]) || (sum >= bwsum[i])) { if ((hashval > bwhash[i]) || (sum > bwsum[i])) (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Quality: %ld (approximate)",i+1); else (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Quality: %ld",i+1); break; } } } } switch (jpeg_info.out_color_space) { case JCS_CMYK: { (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Colorspace: CMYK"); (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Sampling factors: (%d,%d),(%d,%d),(%d,%d),(%d,%d)", jpeg_info.comp_info[0].h_samp_factor, jpeg_info.comp_info[0].v_samp_factor, jpeg_info.comp_info[1].h_samp_factor, jpeg_info.comp_info[1].v_samp_factor, jpeg_info.comp_info[2].h_samp_factor, jpeg_info.comp_info[2].v_samp_factor, jpeg_info.comp_info[3].h_samp_factor, jpeg_info.comp_info[3].v_samp_factor); break; } case JCS_GRAYSCALE: { (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Colorspace: GRAYSCALE"); (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Sampling factors: (%d,%d)", jpeg_info.comp_info[0].h_samp_factor, jpeg_info.comp_info[0].v_samp_factor); break; } case JCS_RGB: { (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Colorspace: RGB"); (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Sampling factors: (%d,%d),(%d,%d),(%d,%d)", jpeg_info.comp_info[0].h_samp_factor, jpeg_info.comp_info[0].v_samp_factor, jpeg_info.comp_info[1].h_samp_factor, jpeg_info.comp_info[1].v_samp_factor, jpeg_info.comp_info[2].h_samp_factor, jpeg_info.comp_info[2].v_samp_factor); break; } default: { (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Colorspace: %d",jpeg_info.out_color_space); (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Sampling factors: (%d,%d),(%d,%d),(%d,%d),(%d,%d)", jpeg_info.comp_info[0].h_samp_factor, jpeg_info.comp_info[0].v_samp_factor, jpeg_info.comp_info[1].h_samp_factor, jpeg_info.comp_info[1].v_samp_factor, jpeg_info.comp_info[2].h_samp_factor, jpeg_info.comp_info[2].v_samp_factor, jpeg_info.comp_info[3].h_samp_factor, jpeg_info.comp_info[3].v_samp_factor); break; } } } if (image_info->ping != MagickFalse) { jpeg_destroy_decompress(&jpeg_info); CloseBlob(image); return(GetFirstImageInList(image)); } if (SetImageExtent(image,0,0) == MagickFalse) { InheritException(exception,&image->exception); return(DestroyImageList(image)); } jpeg_pixels=(JSAMPLE *) AcquireQuantumMemory((size_t) image->columns,jpeg_info.output_components*sizeof(JSAMPLE)); if (jpeg_pixels == (JSAMPLE *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); /* Convert JPEG pixels to pixel packets. */ scanline[0]=(JSAMPROW) jpeg_pixels; for (y=0; y < (long) image->rows; y++) { (void) jpeg_read_scanlines(&jpeg_info,scanline,1); p=jpeg_pixels; q=SetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetIndexes(image); if (jpeg_info.data_precision > 8) { if (jpeg_info.output_components == 1) for (x=0; x < (long) image->columns; x++) { index=ConstrainColormapIndex(image,16*(unsigned long) GETJSAMPLE(*p)); indexes[x]=(IndexPacket) index; *q++=image->colormap[(long) index]; p++; } else for (x=0; x < (long) image->columns; x++) { q->red=ScaleShortToQuantum((unsigned short) (16*GETJSAMPLE(*p++))); q->green=ScaleShortToQuantum((unsigned short) (16*GETJSAMPLE(*p++))); q->blue=ScaleShortToQuantum((unsigned short) (16*GETJSAMPLE(*p++))); if (image->colorspace == CMYKColorspace) indexes[x]=ScaleShortToQuantum((unsigned short) (16*GETJSAMPLE(*p++))); q++; } } else if (jpeg_info.output_components == 1) for (x=0; x < (long) image->columns; x++) { index=ConstrainColormapIndex(image,(unsigned long) GETJSAMPLE(*p)); indexes[x]=(IndexPacket) index; *q++=image->colormap[(long) index]; p++; } else for (x=0; x < (long) image->columns; x++) { q->red=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++)); q->green=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++)); q->blue=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++)); if (image->colorspace == CMYKColorspace) indexes[x]=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++)); q++; } if (SyncImagePixels(image) == MagickFalse) break; if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(y,image->rows) != MagickFalse)) { status=image->progress_monitor(LoadImageTag,y,image->rows, image->client_data); if (status == MagickFalse) break; } } if (jpeg_info.quantize_colors != MagickFalse) for (i=0; i < jpeg_info.actual_number_of_colors; i++) { image->colormap[i].red= ScaleCharToQuantum(jpeg_info.colormap[0][i]); image->colormap[i].green= ScaleCharToQuantum(jpeg_info.colormap[1][i]); image->colormap[i].blue= ScaleCharToQuantum(jpeg_info.colormap[2][i]); } if (image->colorspace == CMYKColorspace) { /* Correct CMYK levels. */ for (y=0; y < (long) image->rows; y++) { q=GetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetIndexes(image); for (x=0; x < (long) image->columns; x++) { q->red=(Quantum) (QuantumRange-q->red); q->green=(Quantum) (QuantumRange-q->green); q->blue=(Quantum) (QuantumRange-q->blue); indexes[x]=(IndexPacket) (QuantumRange-indexes[x]); q++; } if (SyncImagePixels(image) == MagickFalse) break; } } /* Consume Buffer. */ chunk_size = (unsigned long) ((SourceManager *) jpeg_info.src)->source_length; for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); /* Free jpeg resources. */ (void) jpeg_finish_decompress(&jpeg_info); jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels); #else (void) FormatMagickString(message,MaxTextExtent, "AVI compression %s not yet supported",bmp_info.compression); (void) ThrowMagickException(exception,GetMagickModule(), CorruptImageWarning,message,"`%s'",image->filename); for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; #endif } /* Read BMP image */ else { /* Initialize image structure. */ image->columns=avi_info.width; image->rows=avi_info.height; if (SetImageExtent(image,0,0) == MagickFalse) { InheritException(exception,&image->exception); return(DestroyImageList(image)); } image->depth=8; image->units=PixelsPerCentimeterResolution; image->x_resolution=(double) bmp_info.x_pixels/100.0; image->y_resolution=(double) bmp_info.y_pixels/100.0; status=AllocateImageColormap(image,number_colors != 0 ? number_colors : 256); if (status == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); if (number_colors != 0) (void) CopyMagickMemory(image->colormap,colormap,(size_t) number_colors*sizeof(PixelPacket)); if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) if (image->scene >= (image_info->scene+image_info->number_scenes-1)) break; bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32); pixels=(unsigned char *) AcquireQuantumMemory((size_t) image->rows, MagickMax(bytes_per_line,image->columns+256UL)*sizeof(*pixels)); if (pixels == (unsigned char *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); (void) ResetMagickMemory(pixels,0,(size_t) MagickMax( bytes_per_line,image->columns+1)*image->rows); if (LocaleCompare(id,"00db") == 0) { count=ReadBlob(image,(size_t) bytes_per_line*image->rows,pixels); if (count != (ssize_t) (bytes_per_line*image->rows)) ThrowReaderException(CorruptImageError, "InsufficientImageDataInFile"); } else { status=DecodeImage(image,MagickTrue,pixels); if (status == MagickFalse) ThrowReaderException(CorruptImageError, "UnableToRunlengthDecodeImage"); } /* Convert BMP raster image to pixel packets. */ switch ((int) bmp_info.bits_per_pixel) { case 1: { /* Convert bitmap scanline. */ for (y=(long) image->rows-1; y >= 0; y--) { p=pixels+(image->rows-y-1)*bytes_per_line; q=SetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetIndexes(image); for (x=0; x < ((long) image->columns-7); x+=8) { for (bit=0; bit < 8; bit++) { index=(IndexPacket) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00); indexes[x+bit]=index; *q++=image->colormap[(long) index]; } p++; } if ((image->columns % 8) != 0) { for (bit=0; bit < (image->columns % 8); bit++) { index=(IndexPacket) (((*p) & (0x80 >> bit)) != 0 ? 0x01 : 0x00); indexes[x+bit]=index; *q++=image->colormap[(long) index]; } p++; } if (SyncImagePixels(image) == MagickFalse) break; offset=(MagickOffsetType) image->rows-y-1; if (image->previous == (Image *) NULL) if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(offset,image->rows) != MagickFalse)) { status=image->progress_monitor(LoadImageTag,offset, image->rows,image->client_data); if (status == MagickFalse) break; } } break; } case 4: { /* Convert PseudoColor scanline. */ for (y=(long) image->rows-1; y >= 0; y--) { p=pixels+(image->rows-y-1)*bytes_per_line; q=SetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetIndexes(image); for (x=0; x < ((long) image->columns-1); x+=2) { index=ConstrainColormapIndex(image,((*p >> 4) & 0xf)); indexes[x]=index; *q++=image->colormap[(long) index]; index=ConstrainColormapIndex(image,(*p & 0xf)); indexes[x+1]=index; *q++=image->colormap[(long) index]; p++; } if ((image->columns % 2) != 0) { index=ConstrainColormapIndex(image,((*p >> 4) & 0xf)); indexes[x]=index; *q++=image->colormap[(long) index]; p++; } if (SyncImagePixels(image) == MagickFalse) break; offset=(MagickOffsetType) image->rows-y-1; if (image->previous == (Image *) NULL) if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(offset,image->rows) != MagickFalse)) { status=image->progress_monitor(LoadImageTag,offset, image->rows,image->client_data); if (status == MagickFalse) break; } } break; } case 8: { /* Convert PseudoColor scanline. */ bytes_per_line=image->columns; for (y=(long) image->rows-1; y >= 0; y--) { p=pixels+(image->rows-y-1)*bytes_per_line; q=SetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; indexes=GetIndexes(image); for (x=0; x < (long) image->columns; x++) { index=ConstrainColormapIndex(image,*p); indexes[x]=index; *q=image->colormap[(long) index]; p++; q++; } if (SyncImagePixels(image) == MagickFalse) break; offset=(MagickOffsetType) image->rows-y-1; if (image->previous == (Image *) NULL) if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(offset,image->rows) != MagickFalse)) { status=image->progress_monitor(LoadImageTag,offset, image->rows,image->client_data); if (status == MagickFalse) break; } } break; } case 16: { unsigned long word; /* Convert PseudoColor scanline. */ bytes_per_line=image->columns << 1; image->storage_class=DirectClass; for (y=(long) image->rows-1; y >= 0; y--) { p=pixels+(image->rows-y-1)*bytes_per_line; q=SetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; for (x=0; x < (long) image->columns; x++) { word=(unsigned long) (*p++); word|=(*p++ << 8); q->red=ScaleCharToQuantum(ScaleColor5to8((word >> 11) & 0x1f)); q->green=ScaleCharToQuantum(ScaleColor6to8((word >> 5) & 0x3f)); q->blue=ScaleCharToQuantum(ScaleColor5to8(word & 0x1f)); q++; } if (SyncImagePixels(image) == MagickFalse) break; offset=(MagickOffsetType) image->rows-y-1; if (image->previous == (Image *) NULL) if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(offset,image->rows) != MagickFalse)) { status=image->progress_monitor(LoadImageTag,offset, image->rows,image->client_data); if (status == MagickFalse) break; } } break; } case 24: case 32: { /* Convert DirectColor scanline. */ image->storage_class=DirectClass; for (y=(long) image->rows-1; y >= 0; y--) { p=pixels+(image->rows-y-1)*bytes_per_line; q=SetImagePixels(image,0,y,image->columns,1); if (q == (PixelPacket *) NULL) break; for (x=0; x < (long) image->columns; x++) { q->blue=ScaleCharToQuantum(*p++); q->green=ScaleCharToQuantum(*p++); q->red=ScaleCharToQuantum(*p++); if (image->matte != MagickFalse) q->opacity=ScaleCharToQuantum(*p++); q++; } if (SyncImagePixels(image) == MagickFalse) break; offset=(MagickOffsetType) image->rows-y-1; if (image->previous == (Image *) NULL) if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(offset,image->rows) != MagickFalse)) { status=image->progress_monitor(LoadImageTag,offset, image->rows,image->client_data); if (status == MagickFalse) break; } } break; } default: ThrowReaderException(CorruptImageError,"ImproperImageHeader"); } pixels=(unsigned char *) RelinquishMagickMemory(pixels); } /* Setup for next image */ if ((unsigned long) image->scene < (avi_info.total_frames-1)) { /* Allocate next image structure. */ AllocateNextImage(image_info,image); if (GetNextImageInList(image) == (Image *) NULL) { image=DestroyImageList(image); return((Image *) NULL); } image=SyncNextImageInList(image); if (image->progress_monitor != (MagickProgressMonitor) NULL) { status=image->progress_monitor(LoadImagesTag,TellBlob(image), GetBlobSize(image),image->client_data); if (status == MagickFalse) break; } } continue; } if (LocaleCompare(id,"avih") == 0) { /* AVI header. */ if (image_info->verbose != MagickFalse) (void) fprintf(stdout," AVI header\n"); avi_info.delay=ReadBlobLSBLong(image); avi_info.max_data_rate=ReadBlobLSBLong(image); avi_info.pad_granularity=ReadBlobLSBLong(image); avi_info.flags=ReadBlobLSBLong(image); avi_info.total_frames=ReadBlobLSBLong(image); avi_info.initial_frames=ReadBlobLSBLong(image); avi_info.number_streams=ReadBlobLSBLong(image); avi_info.buffer_size=ReadBlobLSBLong(image); avi_info.width=ReadBlobLSBLong(image); avi_info.height=ReadBlobLSBLong(image); avi_info.time_scale=ReadBlobLSBLong(image); avi_info.data_rate=ReadBlobLSBLong(image); avi_info.start_time=ReadBlobLSBLong(image); avi_info.data_length=ReadBlobLSBLong(image); continue; } if (LocaleCompare(id,"idx1") == 0) { for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } if (LocaleCompare(id,"JUNK") == 0) { for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } if (LocaleCompare(id,"LIST") == 0) { /* List of chunks. */ count=ReadBlob(image,4,(unsigned char *) id); if (image_info->verbose != MagickFalse) (void) fprintf(stdout," List type %s\n",id); continue; } if (LocaleCompare(id,"RIFF") == 0) { /* File header. */ count=ReadBlob(image,4,(unsigned char *) id); if (image_info->verbose != MagickFalse) (void) fprintf(stdout," RIFF form type %s\n",id); continue; } if (LocaleCompare(id,"strf") == 0) { /* Stream format. */ if (image_info->verbose != MagickFalse) (void) fprintf(stdout," Stream fcc\n"); if (LocaleCompare(stream_info.data_type,"vids") == 0) { bmp_info.size=ReadBlobLSBLong(image); bmp_info.width=ReadBlobLSBLong(image); bmp_info.height=ReadBlobLSBLong(image); bmp_info.planes=ReadBlobLSBShort(image); bmp_info.bits_per_pixel=ReadBlobLSBShort(image); count=ReadBlob(image,4,(unsigned char *) bmp_info.compression); bmp_info.compression[4]='\0'; bmp_info.image_size=ReadBlobLSBLong(image); bmp_info.x_pixels=ReadBlobLSBLong(image); bmp_info.y_pixels=ReadBlobLSBLong(image); bmp_info.number_colors=ReadBlobLSBLong(image); bmp_info.important_colors=ReadBlobLSBLong(image); chunk_size-=40; number_colors=bmp_info.number_colors; if ((number_colors == 0) && (bmp_info.bits_per_pixel <= 8)) number_colors=1 << bmp_info.bits_per_pixel; if (number_colors != 0) { colormap=(PixelPacket *) AcquireQuantumMemory((size_t) number_colors,sizeof(*colormap)); if (colormap == (PixelPacket *) NULL) ThrowReaderException(ResourceLimitError, "MemoryAllocationFailed"); for (i=0; i < (long) number_colors; i++) { colormap[i].blue=ScaleCharToQuantum((unsigned char) ReadBlobByte(image)); colormap[i].green=ScaleCharToQuantum((unsigned char) ReadBlobByte(image)); colormap[i].red=ScaleCharToQuantum((unsigned char) ReadBlobByte(image)); (void) ReadBlobByte(image); chunk_size-=4; } } for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); if (image_info->verbose != MagickFalse) (void) fprintf(stdout,"Video compression: %s\n", bmp_info.compression); continue; } for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } if (LocaleCompare(id,"strh") == 0) { if (image_info->verbose != MagickFalse) (void) fprintf(stdout," Stream header\n"); count=ReadBlob(image,4,(unsigned char *) stream_info.data_type); stream_info.data_type[4]='\0'; count=ReadBlob(image,4,(unsigned char *) stream_info.data_handler); stream_info.data_handler[4]='\0'; stream_info.flags=ReadBlobLSBLong(image); stream_info.priority=ReadBlobLSBLong(image); stream_info.initial_frames=ReadBlobLSBLong(image); stream_info.time_scale=ReadBlobLSBLong(image); stream_info.data_rate=ReadBlobLSBLong(image); stream_info.start_time=ReadBlobLSBLong(image); stream_info.data_length=ReadBlobLSBLong(image); stream_info.buffer_size=ReadBlobLSBLong(image); stream_info.quality=ReadBlobLSBLong(image); stream_info.sample_size=ReadBlobLSBLong(image); if ((chunk_size & 0x01) != 0) chunk_size++; for (chunk_size-=48; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); if (image_info->verbose != MagickFalse) (void) fprintf(stdout,"AVI Test handler: %s\n", stream_info.data_handler); continue; } if (LocaleCompare(id,"strn") == 0) { for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } if (LocaleCompare(id,"vedt") == 0) { for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } if (strlen(id) == 4 && LocaleCompare(id + 2,"wb") == 0) { /* Skip audio chunks (##wb). */ for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } if (LocaleCompare(id,"ISFT") == 0) { /* Skip Info Chunks. */ for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } if (LocaleCompare(id,"IDIT") == 0) { /* Skip Timecode Information (TODO: seeking). */ for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } (void) FormatMagickString(message,MaxTextExtent, "AVI chunk %s not yet supported",id); (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning, message,"`%s'",image->filename); for ( ; chunk_size != 0; chunk_size--) (void) ReadBlobByte(image); continue; } CloseBlob(image); if ((image->columns == 0) || (image->rows == 0)) { image=DestroyImageList(image); return((Image *) NULL); } return(GetFirstImageInList(image)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e g i s t e r A V I I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RegisterAVIImage() adds properties for the AVI image format to % the list of supported formats. The properties include the image format % tag, a method to read and/or write the format, whether the format % supports the saving of more than one frame to the same file or blob, % whether the format supports native in-memory I/O, and a brief % description of the format. % % The format of the RegisterAVIImage method is: % % unsigned long RegisterAVIImage(void) % */ ModuleExport unsigned long RegisterAVIImage(void) { MagickInfo *entry; entry=SetMagickInfo("AVI"); entry->decoder=(DecodeImageHandler *) ReadAVIImage; entry->magick=(IsImageFormatHandler *) IsAVI; entry->description=ConstantString("Microsoft Audio/Visual Interleaved"); entry->module=ConstantString("AVI"); (void) RegisterMagickInfo(entry); return(MagickImageCoderSignature); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % U n r e g i s t e r A V I I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % UnregisterAVIImage() removes format registrations made by the % AVI module from the list of supported formats. % % The format of the UnregisterAVIImage method is: % % UnregisterAVIImage(void) % */ ModuleExport void UnregisterAVIImage(void) { (void) UnregisterMagickInfo("AVI"); }