diff --git a/modules/imgcodecs/src/grfmt_bmp.cpp b/modules/imgcodecs/src/grfmt_bmp.cpp index 880a8cd105..c838e819f6 100644 --- a/modules/imgcodecs/src/grfmt_bmp.cpp +++ b/modules/imgcodecs/src/grfmt_bmp.cpp @@ -58,6 +58,7 @@ BmpDecoder::BmpDecoder() m_origin = ORIGIN_TL; m_bpp = 0; m_rle_code = BMP_RGB; + initMask(); } @@ -97,6 +98,7 @@ bool BmpDecoder::readHeader() int size = m_strm.getDWord(); CV_Assert(size > 0); // overflow, 2Gb limit + initMask(); if( size >= 36 ) { m_width = m_strm.getDWord(); @@ -107,7 +109,30 @@ bool BmpDecoder::readHeader() m_rle_code = (BmpCompression)m_rle_code_; m_strm.skip(12); int clrused = m_strm.getDWord(); - m_strm.skip( size - 36 ); + + if( m_bpp == 32 && m_rle_code == BMP_BITFIELDS && size >= 56 ) + { + m_strm.skip(4); //important colors + //0 is Red channel bit mask, 1 is Green channel bit mask, 2 is Blue channel bit mask, 3 is Alpha channel bit mask + for( int index_rgba = 0; index_rgba < 4; ++index_rgba ) + { + uint mask = m_strm.getDWord(); + m_rgba_mask[index_rgba] = mask; + if(mask != 0) + { + int bit_count = 0; + while(!(mask & 1)) + { + mask >>= 1; + ++bit_count; + } + m_rgba_bit_offset[index_rgba] = bit_count; + } + } + m_strm.skip( size - 56 ); + } + else + m_strm.skip( size - 36 ); if( m_width > 0 && m_height != 0 && (((m_bpp == 1 || m_bpp == 4 || m_bpp == 8 || @@ -486,8 +511,14 @@ decode_rle8_bad: ; icvCvt_BGRA2Gray_8u_C4C1R( src, 0, data, 0, Size(m_width,1) ); else if( img.channels() == 3 ) icvCvt_BGRA2BGR_8u_C4C3R(src, 0, data, 0, Size(m_width, 1)); - else if( img.channels() == 4 ) - memcpy(data, src, m_width * 4); + else if ( img.channels() == 4 ) + { + bool has_bit_mask = (m_rgba_bit_offset[0] >= 0) && (m_rgba_bit_offset[1] >= 0) && (m_rgba_bit_offset[2] >= 0); + if ( has_bit_mask ) + maskBGRA(data, src, m_width); + else + memcpy(data, src, m_width * 4); + } } result = true; break; @@ -503,7 +534,26 @@ decode_rle8_bad: ; return result; } +void BmpDecoder::initMask() +{ + memset(m_rgba_mask, 0, sizeof(m_rgba_mask)); + memset(m_rgba_bit_offset, -1, sizeof(m_rgba_bit_offset)); +} +void BmpDecoder::maskBGRA(uchar* des, uchar* src, int num) +{ + for( int i = 0; i < num; i++, des += 4, src += 4 ) + { + uint data = *((uint*)src); + des[0] = (uchar)((m_rgba_mask[2] & data) >> m_rgba_bit_offset[2]); + des[1] = (uchar)((m_rgba_mask[1] & data) >> m_rgba_bit_offset[1]); + des[2] = (uchar)((m_rgba_mask[0] & data) >> m_rgba_bit_offset[0]); + if (m_rgba_bit_offset[3] >= 0) + des[3] = (uchar)((m_rgba_mask[3] & data) >> m_rgba_bit_offset[3]); + else + des[3] = 255; + } +} ////////////////////////////////////////////////////////////////////////////////////////// BmpEncoder::BmpEncoder() diff --git a/modules/imgcodecs/src/grfmt_bmp.hpp b/modules/imgcodecs/src/grfmt_bmp.hpp index e94b8ff8b4..4b34124bf7 100644 --- a/modules/imgcodecs/src/grfmt_bmp.hpp +++ b/modules/imgcodecs/src/grfmt_bmp.hpp @@ -73,6 +73,9 @@ public: protected: + void initMask(); + void maskBGRA(uchar* des, uchar* src, int num); + enum Origin { ORIGIN_TL = 0, @@ -85,6 +88,8 @@ protected: int m_bpp; int m_offset; BmpCompression m_rle_code; + uint m_rgba_mask[4]; + int m_rgba_bit_offset[4]; }; diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index 0d28688004..03a8d408d6 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -295,6 +295,32 @@ TEST(Imgcodecs_Bmp, read_rle8) EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), rle, ord); } +TEST(Imgcodecs_Bmp, rgba_bit_mask) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/test_rgba_mask.bmp"; + + const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_8UC4, img.type()); + + const uchar* data = img.ptr(); + ASSERT_EQ(data[3], 255); +} + +TEST(Imgcodecs_Bmp, read_32bit_xrgb) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/test_32bit_xrgb.bmp"; + + const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_8UC4, img.type()); + + const uchar* data = img.ptr(); + ASSERT_EQ(data[3], 255); +} + #ifdef HAVE_IMGCODEC_HDR TEST(Imgcodecs_Hdr, regression) {