% Read/Write Microsoft Windows Bitmap Image Format %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% Glenn Randers-Pehrson %
% December 2001 %
% %
% %
-% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2015 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 %
#include "MagickCore/memory_.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
+#include "MagickCore/option.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/profile.h"
#include "MagickCore/quantum-private.h"
% the decoding process.
%
*/
-
-static inline ssize_t MagickAbsoluteValue(const ssize_t x)
-{
- if (x < 0)
- return(-x);
- return(x);
-}
-
-static inline size_t MagickMax(const size_t x,const size_t y)
-{
- if (x > y)
- return(x);
- return(y);
-}
-
-static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
-{
- if (x < y)
- return(x);
- return(y);
-}
-
static MagickBooleanType DecodeImage(Image *image,const size_t compression,
unsigned char *pixels)
{
q=pixels+(size_t) image->columns*image->rows;
for (y=0; y < (ssize_t) image->rows; )
{
- if ((p < pixels) || (p >= q))
+ MagickBooleanType
+ status;
+
+ if ((p < pixels) || (p > q))
break;
count=ReadBlobByte(image);
if (count == EOF)
Escape mode.
*/
count=ReadBlobByte(image);
+ if (count == EOF)
+ break;
if (count == 0x01)
return(MagickTrue);
switch (count)
}
}
}
- if (SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,image->rows) == MagickFalse)
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
+ image->rows);
+ if (status == MagickFalse)
break;
}
(void) ReadBlobByte(image); /* end of line */
(void) ReadBlobByte(image);
- return(MagickTrue);
+ return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
}
\f
/*
start_position;
MemoryInfo
- *memory_info;
+ *pixel_info;
Quantum
index;
bytes_per_line,
green,
length,
- opacity,
red;
ssize_t
bmp_info.ba_offset=0;
start_position=0;
count=ReadBlob(image,2,magick);
+ if (count != 2)
+ ThrowReaderException(CorruptImageError,"ImproperImageHeader");
do
{
PixelInfo
if (image->debug != MagickFalse)
(void) LogMagickEvent(CoderEvent,GetMagickModule()," Magick: %c%c",
magick[0],magick[1]);
- if ((count == 0) || ((LocaleNCompare((char *) magick,"BM",2) != 0) &&
+ if ((count != 2) || ((LocaleNCompare((char *) magick,"BM",2) != 0) &&
(LocaleNCompare((char *) magick,"CI",2) != 0)))
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
bmp_info.file_size=ReadBlobLSBLong(image);
/*
OS/2 BMP image file.
*/
+ (void) CopyMagickString(image->magick,"BMP2",MaxTextExtent);
bmp_info.width=(ssize_t) ((short) ReadBlobLSBShort(image));
bmp_info.height=(ssize_t) ((short) ReadBlobLSBShort(image));
bmp_info.planes=ReadBlobLSBShort(image);
image->gamma=(bmp_info.gamma_scale.x+bmp_info.gamma_scale.y+
bmp_info.gamma_scale.z)/3.0;
}
+ else
+ (void) CopyMagickString(image->magick,"BMP3",MaxTextExtent);
if (bmp_info.size > 108)
{
ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
if (bmp_info.bits_per_pixel < 16 &&
bmp_info.number_colors > (1U << bmp_info.bits_per_pixel))
- {
- ThrowReaderException(CorruptImageError,
- "UnrecognizedNumberOfColors");
- }
+ ThrowReaderException(CorruptImageError,"UnrecognizedNumberOfColors");
if ((bmp_info.compression == 1) && (bmp_info.bits_per_pixel != 8))
ThrowReaderException(CorruptImageError,"UnrecognizedBitsPerPixel");
if ((bmp_info.compression == 2) && (bmp_info.bits_per_pixel != 4))
image->columns=(size_t) MagickAbsoluteValue(bmp_info.width);
image->rows=(size_t) MagickAbsoluteValue(bmp_info.height);
image->depth=bmp_info.bits_per_pixel <= 8 ? bmp_info.bits_per_pixel : 8;
- image->alpha_trait=(bmp_info.alpha_mask != 0) &&
- (bmp_info.compression == BI_BITFIELDS) ? BlendPixelTrait :
+ image->alpha_trait=((bmp_info.alpha_mask != 0) &&
+ (bmp_info.compression == BI_BITFIELDS)) ? BlendPixelTrait :
UndefinedPixelTrait;
if (bmp_info.bits_per_pixel < 16)
{
if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
if (image->scene >= (image_info->scene+image_info->number_scenes-1))
break;
+ status=SetImageExtent(image,image->columns,image->rows,exception);
+ if (status == MagickFalse)
+ return(DestroyImageList(image));
/*
Read image data.
*/
bmp_info.bits_per_pixel<<=1;
bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32);
length=(size_t) bytes_per_line*image->rows;
- memory_info=AcquireVirtualMemory((size_t) image->rows,
+ pixel_info=AcquireVirtualMemory((size_t) image->rows,
MagickMax(bytes_per_line,image->columns+256UL)*sizeof(*pixels));
- if (memory_info == (MemoryInfo *) NULL)
+ if (pixel_info == (MemoryInfo *) NULL)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
- pixels=(unsigned char *) GetVirtualMemoryBlob(memory_info);
+ pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
if ((bmp_info.compression == BI_RGB) ||
(bmp_info.compression == BI_BITFIELDS))
{
count=ReadBlob(image,length,pixels);
if (count != (ssize_t) length)
{
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
ThrowReaderException(CorruptImageError,
"InsufficientImageDataInFile");
}
status=DecodeImage(image,bmp_info.compression,pixels);
if (status == MagickFalse)
{
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
ThrowReaderException(CorruptImageError,
"UnableToRunlengthDecodeImage");
}
*/
if (bmp_info.compression == BI_RGB)
{
- bmp_info.alpha_mask=image->alpha_trait == BlendPixelTrait ?
+ /*
+ We should ignore the alpha value in BMP3 files but there have been
+ reports about 32 bit files with alpha. We do a quick check to see if
+ the alpha channel contains a value that is not zero (default value).
+ If we find a non zero value we asume the program that wrote the file
+ wants to use the alpha channel.
+ */
+ if ((image->alpha_trait == UndefinedPixelTrait) && (bmp_info.size == 40) &&
+ (bmp_info.bits_per_pixel == 32))
+ {
+ bytes_per_line=4*(image->columns);
+ for (y=(ssize_t) image->rows-1; y >= 0; y--)
+ {
+ p=pixels+(image->rows-y-1)*bytes_per_line;
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ if (*(p+3) != 0)
+ {
+ image->alpha_trait=BlendPixelTrait;
+ y=-1;
+ break;
+ }
+ p+=4;
+ }
+ }
+ }
+ bmp_info.alpha_mask=image->alpha_trait != UndefinedPixelTrait ?
0xff000000U : 0U;
bmp_info.red_mask=0x00ff0000U;
bmp_info.green_mask=0x0000ff00U;
bmp_info.blue_mask=0x0000001fU;
}
}
+ (void) ResetMagickMemory(&shift,0,sizeof(shift));
+ (void) ResetMagickMemory(&quantum_bits,0,sizeof(quantum_bits));
if ((bmp_info.bits_per_pixel == 16) || (bmp_info.bits_per_pixel == 32))
{
register size_t
/*
Get shift and quantum bits info from bitfield masks.
*/
- (void) ResetMagickMemory(&shift,0,sizeof(shift));
- (void) ResetMagickMemory(&quantum_bits,0,sizeof(quantum_bits));
if (bmp_info.red_mask != 0)
while (((bmp_info.red_mask << shift.red) & 0x80000000UL) == 0)
shift.red++;
break;
if (image->previous == (Image *) NULL)
{
- status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
- image->rows);
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
+ (image->rows-y),image->rows);
if (status == MagickFalse)
break;
}
break;
for (x=0; x < ((ssize_t) image->columns-1); x+=2)
{
- index=ConstrainColormapIndex(image,(*p >> 4) & 0x0f,exception);
+ if (IsValidColormapIndex(image,(*p >> 4) & 0x0f,&index,exception)
+ == MagickFalse)
+ break;
SetPixelIndex(image,index,q);
q+=GetPixelChannels(image);
- index=ConstrainColormapIndex(image,*p & 0x0f,exception);
+ if (IsValidColormapIndex(image,*p & 0x0f,&index,exception) ==
+ MagickFalse)
+ break;
SetPixelIndex(image,index,q);
q+=GetPixelChannels(image);
p++;
}
if ((image->columns % 2) != 0)
{
- index=ConstrainColormapIndex(image,(*p >> 4) & 0xf,exception);
+ if (IsValidColormapIndex(image,(*p >> 4) & 0xf,&index,exception)
+ == MagickFalse)
+ break;
SetPixelIndex(image,index,q);
q+=GetPixelChannels(image);
p++;
}
+ if (x < image->columns)
+ break;
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (image->previous == (Image *) NULL)
{
- status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
- image->rows);
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
+ (image->rows-y),image->rows);
if (status == MagickFalse)
break;
}
break;
for (x=(ssize_t) image->columns; x != 0; --x)
{
- index=ConstrainColormapIndex(image,*p++,exception);
+ if (IsValidColormapIndex(image,*p++,&index,exception) ==
+ MagickFalse)
+ break;
SetPixelIndex(image,index,q);
q+=GetPixelChannels(image);
}
+ if (x > 0)
+ break;
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
offset=(MagickOffsetType) (image->rows-y-1);
if (image->previous == (Image *) NULL)
{
- status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
- image->rows);
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
+ (image->rows-y),image->rows);
if (status == MagickFalse)
break;
}
case 16:
{
size_t
+ alpha,
pixel;
/*
if (bmp_info.compression != BI_RGB &&
bmp_info.compression != BI_BITFIELDS)
{
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
ThrowReaderException(CorruptImageError,
"UnrecognizedImageCompression");
}
blue|=((blue & 0xe000) >> 5);
if (quantum_bits.blue <= 8)
blue|=((blue & 0xff00) >> 8);
- opacity=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
- if (quantum_bits.alpha <= 8)
- opacity|=((opacity & 0xff00) >> 8);
SetPixelRed(image,ScaleShortToQuantum((unsigned short) red),q);
SetPixelGreen(image,ScaleShortToQuantum((unsigned short) green),q);
SetPixelBlue(image,ScaleShortToQuantum((unsigned short) blue),q);
SetPixelAlpha(image,OpaqueAlpha,q);
- if (image->alpha_trait == BlendPixelTrait)
- SetPixelAlpha(image,
- ScaleShortToQuantum((unsigned short) opacity),q);
+ if (image->alpha_trait != UndefinedPixelTrait)
+ {
+ alpha=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
+ if (quantum_bits.alpha <= 8)
+ alpha|=((alpha & 0xff00) >> 8);
+ SetPixelAlpha(image,ScaleShortToQuantum(
+ (unsigned short) alpha),q);
+ }
q+=GetPixelChannels(image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
offset=(MagickOffsetType) (image->rows-y-1);
if (image->previous == (Image *) NULL)
{
- status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
- image->rows);
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
+ (image->rows-y),image->rows);
if (status == MagickFalse)
break;
}
offset=(MagickOffsetType) (image->rows-y-1);
if (image->previous == (Image *) NULL)
{
- status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
- image->rows);
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
+ (image->rows-y),image->rows);
if (status == MagickFalse)
break;
}
if ((bmp_info.compression != BI_RGB) &&
(bmp_info.compression != BI_BITFIELDS))
{
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
ThrowReaderException(CorruptImageError,
"UnrecognizedImageCompression");
}
for (y=(ssize_t) image->rows-1; y >= 0; y--)
{
size_t
+ alpha,
pixel;
p=pixels+(image->rows-y-1)*bytes_per_line;
for (x=0; x < (ssize_t) image->columns; x++)
{
pixel=(size_t) (*p++);
- pixel|=(*p++ << 8);
- pixel|=(*p++ << 16);
- pixel|=(*p++ << 24);
+ pixel|=((size_t) *p++ << 8);
+ pixel|=((size_t) *p++ << 16);
+ pixel|=((size_t) *p++ << 24);
red=((pixel & bmp_info.red_mask) << shift.red) >> 16;
if (quantum_bits.red == 8)
red|=(red >> 8);
blue=((pixel & bmp_info.blue_mask) << shift.blue) >> 16;
if (quantum_bits.blue == 8)
blue|=(blue >> 8);
- opacity=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
- if (quantum_bits.alpha == 8)
- opacity|=(opacity >> 8);
SetPixelRed(image,ScaleShortToQuantum((unsigned short) red),q);
SetPixelGreen(image,ScaleShortToQuantum((unsigned short) green),q);
SetPixelBlue(image,ScaleShortToQuantum((unsigned short) blue),q);
SetPixelAlpha(image,OpaqueAlpha,q);
- if (image->alpha_trait == BlendPixelTrait)
- SetPixelAlpha(image,
- ScaleShortToQuantum((unsigned short) opacity),q);
+ if (image->alpha_trait != UndefinedPixelTrait)
+ {
+ alpha=((pixel & bmp_info.alpha_mask) << shift.alpha) >> 16;
+ if (quantum_bits.alpha == 8)
+ alpha|=(alpha >> 8);
+ SetPixelAlpha(image,ScaleShortToQuantum(
+ (unsigned short) alpha),q);
+ }
q+=GetPixelChannels(image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
offset=(MagickOffsetType) (image->rows-y-1);
if (image->previous == (Image *) NULL)
{
- status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
- image->rows);
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
+ (image->rows-y),image->rows);
if (status == MagickFalse)
break;
}
}
default:
{
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
}
}
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
+ if (y > 0)
+ break;
if (EOFBlob(image) != MagickFalse)
{
ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
entry->magick=(IsImageFormatHandler *) IsBMP;
entry->description=ConstantString("Microsoft Windows bitmap image");
entry->module=ConstantString("BMP");
- entry->adjoin=MagickFalse;
- entry->seekable_stream=MagickTrue;
+ entry->flags^=CoderAdjoinFlag;
+ entry->flags|=CoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
entry=SetMagickInfo("BMP2");
entry->encoder=(EncodeImageHandler *) WriteBMPImage;
entry->magick=(IsImageFormatHandler *) IsBMP;
entry->description=ConstantString("Microsoft Windows bitmap image (V2)");
entry->module=ConstantString("BMP");
- entry->adjoin=MagickFalse;
- entry->seekable_stream=MagickTrue;
+ entry->flags^=CoderAdjoinFlag;
+ entry->flags|=CoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
entry=SetMagickInfo("BMP3");
entry->encoder=(EncodeImageHandler *) WriteBMPImage;
entry->magick=(IsImageFormatHandler *) IsBMP;
entry->description=ConstantString("Microsoft Windows bitmap image (V3)");
entry->module=ConstantString("BMP");
- entry->adjoin=MagickFalse;
- entry->seekable_stream=MagickTrue;
+ entry->flags^=CoderAdjoinFlag;
+ entry->flags|=CoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
{
BMPInfo
bmp_info;
+ const char
+ *value;
+
const StringInfo
*profile;
scene;
MemoryInfo
- *memory_info;
+ *pixel_info;
register const Quantum
*p;
else
if (LocaleCompare(image_info->magick,"BMP3") == 0)
type=3;
+
+ value=GetImageOption(image_info,"bmp:format");
+
+ if (value != (char *) NULL)
+ {
+ (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+ " Format=%s",value);
+
+ if (LocaleCompare(value,"bmp2") == 0)
+ type=2;
+ if (LocaleCompare(value,"bmp3") == 0)
+ type=3;
+ if (LocaleCompare(value,"bmp4") == 0)
+ type=4;
+ }
+
scene=0;
do
{
/*
Initialize BMP raster file header.
*/
- if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
- (void) TransformImageColorspace(image,sRGBColorspace,exception);
+ (void) TransformImageColorspace(image,sRGBColorspace,exception);
(void) ResetMagickMemory(&bmp_info,0,sizeof(bmp_info));
bmp_info.file_size=14+12;
if (type > 2)
if (image_info->compression == RLECompression)
bmp_info.bits_per_pixel=8;
bmp_info.number_colors=1U << bmp_info.bits_per_pixel;
- if (image->alpha_trait == BlendPixelTrait)
+ if (image->alpha_trait != UndefinedPixelTrait)
(void) SetImageStorageClass(image,DirectClass,exception);
else
if ((size_t) bmp_info.number_colors < image->colors)
*/
bmp_info.number_colors=0;
bmp_info.bits_per_pixel=(unsigned short)
- ((type > 3) && (image->alpha_trait == BlendPixelTrait) ? 32 : 24);
+ ((type > 3) && (image->alpha_trait != UndefinedPixelTrait) ? 32 : 24);
bmp_info.compression=(unsigned int) ((type > 3) &&
- (image->alpha_trait == BlendPixelTrait) ? BI_BITFIELDS : BI_RGB);
+ (image->alpha_trait != UndefinedPixelTrait) ? BI_BITFIELDS : BI_RGB);
}
bytes_per_line=4*((image->columns*bmp_info.bits_per_pixel+31)/32);
bmp_info.ba_offset=0;
if (type == 2)
bmp_info.size=12;
else
- if ((type == 3) || ((image->alpha_trait != BlendPixelTrait) &&
+ if ((type == 3) || ((image->alpha_trait == UndefinedPixelTrait) &&
(have_color_info == MagickFalse)))
{
type=3;
/*
Convert MIFF to BMP raster pixels.
*/
- memory_info=AcquireVirtualMemory((size_t) bmp_info.image_size,
+ pixel_info=AcquireVirtualMemory((size_t) bmp_info.image_size,
sizeof(*pixels));
- if (memory_info == (MemoryInfo *) NULL)
+ if (pixel_info == (MemoryInfo *) NULL)
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
- pixels=(unsigned char *) GetVirtualMemoryBlob(memory_info);
+ pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
(void) ResetMagickMemory(pixels,0,(size_t) bmp_info.image_size);
switch (bmp_info.bits_per_pixel)
{
(image->rows+2)*sizeof(*pixels));
if (rle_info == (MemoryInfo *) NULL)
{
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
}
bmp_data=(unsigned char *) GetVirtualMemoryBlob(rle_info);
bmp_info.image_size=(unsigned int) EncodeImage(image,bytes_per_line,
pixels,bmp_data);
bmp_info.file_size+=bmp_info.image_size;
- memory_info=RelinquishVirtualMemory(memory_info);
- memory_info=rle_info;
+ pixel_info=RelinquishVirtualMemory(pixel_info);
+ pixel_info=rle_info;
pixels=bmp_data;
bmp_info.compression=BI_RLE8;
}
" Storage class=PseudoClass");
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Image depth=%.20g",(double) image->depth);
- if (image->alpha_trait == BlendPixelTrait)
+ if (image->alpha_trait != UndefinedPixelTrait)
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Matte=True");
else
(void) WriteBlobLSBLong(image,bmp_info.number_colors);
(void) WriteBlobLSBLong(image,bmp_info.colors_important);
}
- if ((type > 3) && ((image->alpha_trait == BlendPixelTrait) ||
+ if ((type > 3) && ((image->alpha_trait != UndefinedPixelTrait) ||
(have_color_info != MagickFalse)))
{
/*
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" Pixels: %u bytes",bmp_info.image_size);
(void) WriteBlob(image,(size_t) bmp_info.image_size,pixels);
- memory_info=RelinquishVirtualMemory(memory_info);
+ pixel_info=RelinquishVirtualMemory(pixel_info);
if (GetNextImageInList(image) == (Image *) NULL)
break;
image=SyncNextImageInList(image);