/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% 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 <setjmp.h>
#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");
}
|