From d78e9fb1cb7718f9d62c9ad400a97dea23b179bd Mon Sep 17 00:00:00 2001 From: Brian Nixon Date: Sat, 3 Jun 2023 00:54:02 +0100 Subject: [PATCH] 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. --- build/cmake/tests/gui/CMakeLists.txt | 1 + src/common/imagbmp.cpp | 27 ++++++++++++++++++++---- src/msw/dib.cpp | 9 ++++++-- tests/Makefile.in | 2 +- tests/image/bitfields-alpha.bmp | Bin 0 -> 4162 bytes tests/image/image.cpp | 30 ++++++++++++++++++++------- tests/makefile.gcc | 2 +- tests/makefile.vc | 2 +- tests/test.bkl | 1 + 9 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 tests/image/bitfields-alpha.bmp diff --git a/build/cmake/tests/gui/CMakeLists.txt b/build/cmake/tests/gui/CMakeLists.txt index 72217949b6..9ef1076103 100644 --- a/build/cmake/tests/gui/CMakeLists.txt +++ b/build/cmake/tests/gui/CMakeLists.txt @@ -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 diff --git a/src/common/imagbmp.cpp b/src/common/imagbmp.cpp index 382103c523..f46ba6b284 100644 --- a/src/common/imagbmp.cpp +++ b/src/common/imagbmp.cpp @@ -510,7 +510,7 @@ struct BMPDesc wxScopedArray 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; diff --git a/src/msw/dib.cpp b/src/msw/dib.cpp index 2b390c3779..3c8cda7263 100644 --- a/src/msw/dib.cpp +++ b/src/msw/dib.cpp @@ -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(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(pbmih) + pbmih->biSize + numColors * sizeof(RGBQUAD); } HBITMAP hbmp = ::CreateDIBitmap diff --git a/tests/Makefile.in b/tests/Makefile.in index c1aaca8f35..1adca31cda 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -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` ; \ diff --git a/tests/image/bitfields-alpha.bmp b/tests/image/bitfields-alpha.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b4ad4e49286e65ee37745c5f540103b96a83ec36 GIT binary patch literal 4162 zcmeIup>HzU8NlI^r2^4phzbO`Ly!!CKp+q#<4X_-1OkB|HwZ)p0)eO?83KVoAP@+$ zR8$}k2t-AbArJ@z0)Zem2t)+}QBgt8o@|r*6ZYnulk>>m`>XQn&kz5eU*5mEfBEl? z`2ETMe@}RL_|JKG=>PHXx4}Oi{yzNu;h&@59v;R#;VIAfjo*3B3tsYyKlqc^yx}eH zc+Uqu@`=xU;V-`OjlcPa?;PPrj`9=7_?hFJ;3TIw%^A*ej`LjLBA2+#6|QoP>)hZb zxA=wI+~F?2a*z8w;31EA%oCpSjNka3=e*!0ulR#MdCeQ%@{ad>;3J>-%oqORE8qB= zfB4Q3e&i@Wag3ii&IwL(iqo9oEay1S1uk-l%Ut0q*SO9NZgPuXxXm5z@+&kNZ5}A&+>>6Q1&n-}s&9yx=9T z_=7)r%^TkGj`w`vBcJ%p7yjZa-}sw<_|6f2*> zT;VF$xXul3a*JQM%^mLYEBCn110M2-$2{RF&-jhsdCm)7@`^wBlh?fAE$?{G2R`zN z&wSx8zVeN~`G@Zu;YW`06UX?OJFr&I?}hia+?1*Sz5^?|9D#KJtmreBm#?@{PawhwmKWM~?Cn$M~7!oZuv!>%{Xu`wAM^+PL4VL6 z^auSxf6yQF2mL{R&>!>%{Xu`wAM^+PL4VL6^auSxf6yQF2mL{R&>!>%{Xu`wAM^+P zL4VL6^auSxf6yQF2mL{R&>!>%{o()p_A#)Jp?!?(W6Tqt@{Hg3o#(vZC9n8{KY7g? z-tvz3eBdLW_{)hZbxA=wI+~F?2a*z8w z;31EA%oCpSjNka3=e*!0ulR#MdCeQ%@{ad>;3J>-%oqORE8qB=fB4Q3e&i@Wag3ii z&IwL(iqo9oEay1S1uk-l%Ut0q*SO9NZgPuXxXm5z@+&kNZ5}A&+>>6Q1&n-}s&9yx=9T_=7)r%^TkGj`w`v zBcJ%p7yjZa-}sw<_|6f2*>T;VF$xXul3a*JQM z%^mLYEBCn110M2-$2{RF&-jhsdCm)7@`^wBlh?fAE$?{G2R`zN&wSx8zVeN~`G@Zu y;YW`06UX?O(&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"); diff --git a/tests/makefile.gcc b/tests/makefile.gcc index b72eb94599..138bf16b3b 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -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 diff --git a/tests/makefile.vc b/tests/makefile.vc index 73f03802d7..3d76c9ebbb 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -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 diff --git a/tests/test.bkl b/tests/test.bkl index 5bd7328665..0b8e222333 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -354,6 +354,7 @@ image bitfields.bmp + bitfields-alpha.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp