Correct loading of BI_BITFIELDS bitmaps

This fixes several minor bugs in the loading of `BI_BITFIELDS` bitmaps,
related to handling different header sizes and the colour and alpha
masks.

Closes #23601.
This commit is contained in:
Brian Nixon 2023-06-03 00:54:02 +01:00 committed by Vadim Zeitlin
parent 6bba7055aa
commit d78e9fb1cb
9 changed files with 57 additions and 17 deletions

View file

@ -148,6 +148,7 @@ set(TEST_GUI_DATA
horse.tif
horse.xpm
image/bitfields.bmp
image/bitfields-alpha.bmp
image/8bpp-colorsused-large.bmp
image/8bpp-colorsused-negative.bmp
image/rle4-delta-320x240.bmp

View file

@ -510,7 +510,7 @@ struct BMPDesc
wxScopedArray<BMPPalette> paletteData;
int rmask, gmask, bmask, amask;
int rmask, gmask, bmask;
};
// Read the data in BMP format into the given image.
@ -620,6 +620,18 @@ bool LoadBMPData(wxImage * image, const BMPDesc& desc,
gmask = desc.gmask;
bmask = desc.bmask;
// Mimic Windows behaviour: alpha is applied only in 32bpp
// and if the colour masks are the same as for BI_RGB.
// Any alpha mask in the header is ignored.
if ( bpp == 32 &&
rmask == 0x00FF0000 &&
gmask == 0x0000FF00 &&
bmask == 0x000000FF )
{
amask = 0xFF000000;
ashift = 24;
}
// find shift amount (Least significant bit of mask)
for (bit = bpp-1; bit>=0; bit--)
{
@ -1207,15 +1219,18 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream,
if ( desc.comp == BI_BITFIELDS )
{
// Read the mask values from the header.
if ( !stream.ReadAll(dbuf, 4 * 4) )
if ( !stream.ReadAll(dbuf, 4 * 3) )
return false;
hdrBytesRead += 4 * 4;
hdrBytesRead += 4 * 3;
desc.rmask = wxINT32_SWAP_ON_BE(dbuf[0]);
desc.gmask = wxINT32_SWAP_ON_BE(dbuf[1]);
desc.bmask = wxINT32_SWAP_ON_BE(dbuf[2]);
desc.amask = wxINT32_SWAP_ON_BE(dbuf[3]);
// There will also be an alpha mask if (and only if) the header is
// V4 or V5, so we mustn't try to read it if we don't have one of
// those. But it's not used anywhere in any case.
}
// Now that we've read everything we needed from the header, advance
@ -1231,6 +1246,10 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream,
// bytes preceding it: "BM" signature and 3 other DWORDs.
wxFileOffset bytesRead = 14 + hdrSize;
// We might have read colour masks.
if ( desc.comp == BI_BITFIELDS )
bytesRead += 12;
// We must have a palette for 1bpp, 4bpp and 8bpp bitmaps.
if (desc.ncolors == 0 && desc.bpp < 16)
desc.ncolors = 1 << desc.bpp;

View file

@ -416,7 +416,9 @@ HBITMAP wxDIB::ConvertToBitmap(const BITMAPINFO *pbmi, HDC hdc, const void *bits
switch ( pbmih->biCompression )
{
case BI_BITFIELDS:
numColors = 3;
// with a classic BITMAPINFOHEADER, there are 3 colour-mask DWORDs
// after the header. Otherwise the masks are part of the header
numColors = pbmih->biSize == sizeof(BITMAPINFOHEADER) ? 3 : 0;
break;
case BI_RGB:
@ -436,7 +438,10 @@ HBITMAP wxDIB::ConvertToBitmap(const BITMAPINFO *pbmi, HDC hdc, const void *bits
numColors = 0;
}
bits = reinterpret_cast<const char*>(pbmih + 1) + numColors * sizeof(RGBQUAD);
// pbmih->biSize might not be the same as sizeof(BITMAPINFOHEADER)
// (such as in the case of a BITMAPV4HEADER or BITMAPV5HEADER);
// we need to advance by the number of bytes actually present
bits = reinterpret_cast<const char*>(pbmih) + pbmih->biSize + numColors * sizeof(RGBQUAD);
}
HBITMAP hbmp = ::CreateDIBitmap

View file

@ -582,7 +582,7 @@ data-image-sample:
data-images:
@mkdir -p image
@for f in bitfields.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp rle8-delta-320x240.bmp rle8-delta-320x240-expected.bmp horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png wx.png toucan.png toucan_hue_0.538.png toucan_sat_-0.41.png toucan_bright_-0.259.png toucan_hsv_0.538_-0.41_-0.259.png toucan_light_46.png toucan_dis_240.png toucan_grey.png toucan_mono_255_255_255.png width-times-height-overflow.bmp width_height_32_bit_overflow.pgm; do \
@for f in bitfields.bmp bitfields-alpha.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp rle8-delta-320x240.bmp rle8-delta-320x240-expected.bmp horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png wx.png toucan.png toucan_hue_0.538.png toucan_sat_-0.41.png toucan_bright_-0.259.png toucan_hsv_0.538_-0.41_-0.259.png toucan_light_46.png toucan_dis_240.png toucan_grey.png toucan_mono_255_255_255.png width-times-height-overflow.bmp width_height_32_bit_overflow.pgm; do \
if test ! -f image/$$f -a ! -d image/$$f ; \
then x=yep ; \
else x=`find $(srcdir)/image/$$f -newer image/$$f -print` ; \

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -1331,12 +1331,20 @@ static wxImage ImageFromBMPFile(const wxString& filename)
// wxDIB::ConvertToBitmap() about it because we need to test its
// code path where bits==nullptr, so we just have to fail the test
const BITMAPINFO* pbmi = reinterpret_cast<const BITMAPINFO*>(&buf[0]);
wxUint32 nColors = pbmi->bmiHeader.biClrUsed
? pbmi->bmiHeader.biClrUsed
: (pbmi->bmiHeader.biBitCount <= 8
? 1 << pbmi->bmiHeader.biBitCount
: 0);
wxUint32 cbColorTable = nColors * sizeof(RGBQUAD);
wxUint32 numColors = 0;
if ( pbmi->bmiHeader.biCompression == BI_BITFIELDS )
{
if ( pbmi->bmiHeader.biSize == sizeof(BITMAPINFOHEADER) )
numColors = 3;
}
else
{
if ( pbmi->bmiHeader.biClrUsed )
numColors = pbmi->bmiHeader.biClrUsed;
else if ( pbmi->bmiHeader.biBitCount <= 8 )
numColors = 1 << pbmi->bmiHeader.biBitCount;
}
wxUint32 cbColorTable = numColors * sizeof(RGBQUAD);
size_t ofstComputed = hdrLen + pbmi->bmiHeader.biSize + cbColorTable;
REQUIRE(ofstComputed == ofstDeclared);
@ -1350,7 +1358,7 @@ static wxImage ImageFromBMPFile(const wxString& filename)
// Compare the results of loading a BMP via wxBMPHandler
// and via Windows' own conversion (CreateDIBitmap())
static void CompareBMPImageLoad(const wxString& filename)
static void CompareBMPImageLoad(const wxString& filename, int properties = 0)
{
wxImage image1 = ImageFromBMPFile(filename);
REQUIRE( image1.IsOk() );
@ -1359,11 +1367,17 @@ static void CompareBMPImageLoad(const wxString& filename)
REQUIRE( image2.IsOk() );
INFO("Comparing loading methods for " << filename);
CompareImage(*wxImage::FindHandler(wxBITMAP_TYPE_BMP), image1, 0, &image2);
CompareImage(*wxImage::FindHandler(wxBITMAP_TYPE_BMP),
image1, properties, &image2);
}
TEST_CASE_METHOD(ImageHandlersInit, "wxImage::BMPLoadMethod", "[image][bmp]")
{
CompareBMPImageLoad("image/bitfields.bmp");
// We would check the alpha on this one, but at the moment it fails
// because of the way CompareImage() saves and reloads images,
// which for BMPs does not preserve the alpha channel
CompareBMPImageLoad("image/bitfields-alpha.bmp"/*, wxIMAGE_HAVE_ALPHA*/);
CompareBMPImageLoad("image/horse_grey.bmp");
CompareBMPImageLoad("image/horse_rle8.bmp");
CompareBMPImageLoad("image/horse_rle4.bmp");

View file

@ -552,7 +552,7 @@ data-image-sample:
data-images:
if not exist image mkdir image
for %%f in (bitfields.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp rle8-delta-320x240.bmp rle8-delta-320x240-expected.bmp horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png wx.png toucan.png toucan_hue_0.538.png toucan_sat_-0.41.png toucan_bright_-0.259.png toucan_hsv_0.538_-0.41_-0.259.png toucan_light_46.png toucan_dis_240.png toucan_grey.png toucan_mono_255_255_255.png width-times-height-overflow.bmp width_height_32_bit_overflow.pgm) do if not exist image\%%f copy .\image\%%f image
for %%f in (bitfields.bmp bitfields-alpha.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp rle8-delta-320x240.bmp rle8-delta-320x240-expected.bmp horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png wx.png toucan.png toucan_hue_0.538.png toucan_sat_-0.41.png toucan_bright_-0.259.png toucan_hsv_0.538_-0.41_-0.259.png toucan_light_46.png toucan_dis_240.png toucan_grey.png toucan_mono_255_255_255.png width-times-height-overflow.bmp width_height_32_bit_overflow.pgm) do if not exist image\%%f copy .\image\%%f image
fr:
if not exist $(OBJS)\intl\fr mkdir $(OBJS)\intl\fr

View file

@ -845,7 +845,7 @@ data-image-sample:
data-images:
if not exist image mkdir image
for %f in (bitfields.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp rle8-delta-320x240.bmp rle8-delta-320x240-expected.bmp horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png wx.png toucan.png toucan_hue_0.538.png toucan_sat_-0.41.png toucan_bright_-0.259.png toucan_hsv_0.538_-0.41_-0.259.png toucan_light_46.png toucan_dis_240.png toucan_grey.png toucan_mono_255_255_255.png width-times-height-overflow.bmp width_height_32_bit_overflow.pgm) do if not exist image\%f copy .\image\%f image
for %f in (bitfields.bmp bitfields-alpha.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp rle8-delta-320x240.bmp rle8-delta-320x240-expected.bmp horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png wx.png toucan.png toucan_hue_0.538.png toucan_sat_-0.41.png toucan_bright_-0.259.png toucan_hsv_0.538_-0.41_-0.259.png toucan_light_46.png toucan_dis_240.png toucan_grey.png toucan_mono_255_255_255.png width-times-height-overflow.bmp width_height_32_bit_overflow.pgm) do if not exist image\%f copy .\image\%f image
fr:
if not exist $(OBJS)\intl\fr mkdir $(OBJS)\intl\fr

View file

@ -354,6 +354,7 @@
<dstdir>image</dstdir>
<files>
bitfields.bmp
bitfields-alpha.bmp
8bpp-colorsused-large.bmp
8bpp-colorsused-negative.bmp
rle4-delta-320x240.bmp