1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge pull request #27559 from Kumataro:fix27555

imgcodecs: bmp: support to write 32bpp BMP with BI_BITFIELDS #27559

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Kumataro
2025-09-09 15:23:43 +09:00
committed by GitHub
parent ee41f46b46
commit 9196edd299
4 changed files with 145 additions and 4 deletions
+57 -3
View File
@@ -42,6 +42,7 @@
#include "precomp.hpp"
#include "grfmt_bmp.hpp"
#include "opencv2/core/utils/logger.hpp"
namespace cv
{
@@ -603,6 +604,7 @@ BmpEncoder::BmpEncoder()
{
m_description = "Windows bitmap (*.bmp;*.dib)";
m_buf_supported = true;
m_supported_encode_key = {IMWRITE_BMP_COMPRESSION};
}
@@ -615,11 +617,12 @@ ImageEncoder BmpEncoder::newEncoder() const
return makePtr<BmpEncoder>();
}
bool BmpEncoder::write( const Mat& img, const std::vector<int>& )
bool BmpEncoder::write( const Mat& img, const std::vector<int>& params )
{
int width = img.cols, height = img.rows, channels = img.channels();
int fileStep = (width*channels + 3) & -4;
uchar zeropad[] = "\0\0\0\0";
WLByteStream strm;
if( m_buf )
@@ -630,7 +633,35 @@ bool BmpEncoder::write( const Mat& img, const std::vector<int>& )
else if( !strm.open( m_filename ))
return false;
int bitmapHeaderSize = 40;
// sRGB colorspace requires BITMAPV5HEADER.
// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header
bool useV5BitFields = true;
for(size_t i = 0 ; i < params.size(); i++)
{
const int value = params[i+1];
switch(params[i])
{
case IMWRITE_BMP_COMPRESSION:
{
switch(value) {
case IMWRITE_BMP_COMPRESSION_RGB:
useV5BitFields = false;
break;
case IMWRITE_BMP_COMPRESSION_BITFIELDS:
useV5BitFields = true;
break;
default:
useV5BitFields = true;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_BMP_COMPRESSION must be one of ImwriteBMPCompressionFlags. It is fallbacked to true", value));
break;
}
}
break;
}
}
useV5BitFields &= (channels == 4); // BMP_BITFIELDS requires 32 bit per pixel.
int bitmapHeaderSize = useV5BitFields ? 124 : 40;
int paletteSize = channels > 1 ? 0 : 1024;
int headerSize = 14 /* fileheader */ + bitmapHeaderSize + paletteSize;
size_t fileSize = (size_t)fileStep*height + headerSize;
@@ -653,13 +684,36 @@ bool BmpEncoder::write( const Mat& img, const std::vector<int>& )
CHECK_WRITE(strm.putDWord( height ));
CHECK_WRITE(strm.putWord( 1 ));
CHECK_WRITE(strm.putWord( channels << 3 ));
CHECK_WRITE(strm.putDWord( BMP_RGB ));
CHECK_WRITE(strm.putDWord( useV5BitFields ? BMP_BITFIELDS : BMP_RGB ));
CHECK_WRITE(strm.putDWord( 0 ));
CHECK_WRITE(strm.putDWord( 0 ));
CHECK_WRITE(strm.putDWord( 0 ));
CHECK_WRITE(strm.putDWord( 0 ));
CHECK_WRITE(strm.putDWord( 0 ));
if( useV5BitFields )
{
CHECK_WRITE(strm.putDWord( 0x00FF0000 )); // bV5RedMask
CHECK_WRITE(strm.putDWord( 0x0000FF00 )); // bV5GreenMask
CHECK_WRITE(strm.putDWord( 0x000000FF )); // bV5BlueMask
CHECK_WRITE(strm.putDWord( 0xFF000000 )); // bV5AlphaMask
CHECK_WRITE(strm.putBytes( "BGRs", 4)); // bV5CSType (sRGB)
{ // bV5Endpoints
for(int index_rgb = 0; index_rgb < 3; index_rgb ++ ){ // Red/Green/Blue
CHECK_WRITE(strm.putDWord( 0 )); // ciexyzX
CHECK_WRITE(strm.putDWord( 0 )); // ciexyzY
CHECK_WRITE(strm.putDWord( 0 )); // ciexyzZ
}
}
CHECK_WRITE(strm.putDWord( 0 )); // bV5GammaRed
CHECK_WRITE(strm.putDWord( 0 )); // bV5GammaGreen
CHECK_WRITE(strm.putDWord( 0 )); // bV5GammaBlue
CHECK_WRITE(strm.putDWord( 0 )); // bV5Intent
CHECK_WRITE(strm.putDWord( 0 )); // bV5ProfileData
CHECK_WRITE(strm.putDWord( 0 )); // bV5ProfileSize
CHECK_WRITE(strm.putDWord( 0 )); // bV5Reserved
}
if( channels == 1 )
{
FillGrayPalette( palette, 8 );