Fix handling of mask in wxBitmap::ConvertToImage()
Handling mask when converting wxBitmap to wxImage was accidentally
broken by 7e9afad53a
(Add real support for monochrome bitmaps to wxMSW,
2020-10-19). Fix this now and reuse the new wxMonoPixelData to make the
code simpler and more readable.
This commit is best viewed ignoring whitespace-only changes, as it
unindents a large block of code.
Closes https://github.com/wxWidgets/wxWidgets/pull/2125
Closes #18974, #18975.
This commit is contained in:
@@ -968,67 +968,58 @@ wxImage wxBitmap::ConvertToImage() const
|
|||||||
return wxNullImage;
|
return wxNullImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// now do the same for the mask, if we have any
|
// now handle the mask, if we have any
|
||||||
HBITMAP hbmpMask = GetMask() ? (HBITMAP) GetMask()->GetMaskBitmap() : NULL;
|
if ( GetMask() )
|
||||||
if ( hbmpMask )
|
|
||||||
{
|
{
|
||||||
wxDIB dibMask(hbmpMask);
|
// we hard code the mask colour for now but we could also make an
|
||||||
if ( dibMask.IsOk() )
|
// effort (and waste time) to choose a colour not present in the
|
||||||
|
// image already to avoid having to fudge the pixels below --
|
||||||
|
// whether it's worth to do it is unclear however
|
||||||
|
static const int MASK_RED = 1;
|
||||||
|
static const int MASK_GREEN = 2;
|
||||||
|
static const int MASK_BLUE = 3;
|
||||||
|
static const int MASK_BLUE_REPLACEMENT = 2;
|
||||||
|
|
||||||
|
wxBitmap bmpMask(GetMask()->GetBitmap());
|
||||||
|
wxMonoPixelData dataMask(bmpMask);
|
||||||
|
const int h = dataMask.GetHeight();
|
||||||
|
const int w = dataMask.GetWidth();
|
||||||
|
unsigned char* data = image.GetData();
|
||||||
|
|
||||||
|
wxMonoPixelData::Iterator rowStart(dataMask);
|
||||||
|
for ( int y = 0; y < h; ++y )
|
||||||
{
|
{
|
||||||
// TODO: use wxRawBitmap to iterate over DIB
|
// traverse one mask line
|
||||||
|
wxMonoPixelData::Iterator p = rowStart;
|
||||||
// we hard code the mask colour for now but we could also make an
|
for ( int x = 0; x < w; ++x )
|
||||||
// effort (and waste time) to choose a colour not present in the
|
|
||||||
// image already to avoid having to fudge the pixels below --
|
|
||||||
// whether it's worth to do it is unclear however
|
|
||||||
static const int MASK_RED = 1;
|
|
||||||
static const int MASK_GREEN = 2;
|
|
||||||
static const int MASK_BLUE = 3;
|
|
||||||
static const int MASK_BLUE_REPLACEMENT = 2;
|
|
||||||
|
|
||||||
const int h = dibMask.GetHeight();
|
|
||||||
const int w = dibMask.GetWidth();
|
|
||||||
const int bpp = dibMask.GetDepth();
|
|
||||||
const int maskBytesPerPixel = bpp >> 3;
|
|
||||||
const int maskBytesPerLine = wxDIB::GetLineSize(w, bpp);
|
|
||||||
unsigned char *data = image.GetData();
|
|
||||||
|
|
||||||
// remember that DIBs are stored in bottom to top order
|
|
||||||
unsigned char *
|
|
||||||
maskLineStart = dibMask.GetData() + ((h - 1) * maskBytesPerLine);
|
|
||||||
|
|
||||||
for ( int y = 0; y < h; y++, maskLineStart -= maskBytesPerLine )
|
|
||||||
{
|
{
|
||||||
// traverse one mask DIB line
|
// should this pixel be transparent?
|
||||||
unsigned char *mask = maskLineStart;
|
if ( p.Pixel() )
|
||||||
for ( int x = 0; x < w; x++, mask += maskBytesPerPixel )
|
|
||||||
{
|
{
|
||||||
// should this pixel be transparent?
|
// no, check that it isn't transparent by accident
|
||||||
if ( *mask )
|
if ( (data[0] == MASK_RED) &&
|
||||||
|
(data[1] == MASK_GREEN) &&
|
||||||
|
(data[2] == MASK_BLUE) )
|
||||||
{
|
{
|
||||||
// no, check that it isn't transparent by accident
|
// we have to fudge the colour a bit to prevent
|
||||||
if ( (data[0] == MASK_RED) &&
|
// this pixel from appearing transparent
|
||||||
(data[1] == MASK_GREEN) &&
|
data[2] = MASK_BLUE_REPLACEMENT;
|
||||||
(data[2] == MASK_BLUE) )
|
}
|
||||||
{
|
|
||||||
// we have to fudge the colour a bit to prevent
|
|
||||||
// this pixel from appearing transparent
|
|
||||||
data[2] = MASK_BLUE_REPLACEMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
data += 3;
|
data += 3;
|
||||||
}
|
|
||||||
else // yes, transparent pixel
|
|
||||||
{
|
|
||||||
*data++ = MASK_RED;
|
|
||||||
*data++ = MASK_GREEN;
|
|
||||||
*data++ = MASK_BLUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else // yes, transparent pixel
|
||||||
|
{
|
||||||
|
*data++ = MASK_RED;
|
||||||
|
*data++ = MASK_GREEN;
|
||||||
|
*data++ = MASK_BLUE;
|
||||||
|
}
|
||||||
|
++p;
|
||||||
}
|
}
|
||||||
|
rowStart.OffsetY(dataMask, 1);
|
||||||
image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
image.SetMaskColour(MASK_RED, MASK_GREEN, MASK_BLUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
@@ -126,6 +126,59 @@ TEST_CASE("BitmapTestCase::Mask", "[bitmap][mask]")
|
|||||||
REQUIRE(bmp.GetMask() == mask2);
|
REQUIRE(bmp.GetMask() == mask2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("BitmapTestCase::ConvertToImageWithMask", "[bitmap][image][mask]")
|
||||||
|
{
|
||||||
|
wxBitmap color(32, 32, 24);
|
||||||
|
{
|
||||||
|
wxMemoryDC dc(color);
|
||||||
|
dc.SetPen(*wxYELLOW_PEN);
|
||||||
|
dc.SetBrush(*wxYELLOW_BRUSH);
|
||||||
|
dc.DrawRectangle(0, 0, color.GetWidth(), color.GetHeight());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
wxBitmap bmask(color.GetWidth(), color.GetHeight(), 1);
|
||||||
|
{
|
||||||
|
wxMemoryDC dc(bmask);
|
||||||
|
#if wxUSE_GRAPHICS_CONTEXT
|
||||||
|
wxGraphicsContext* gc = dc.GetGraphicsContext();
|
||||||
|
if (gc)
|
||||||
|
{
|
||||||
|
gc->SetAntialiasMode(wxANTIALIAS_NONE);
|
||||||
|
}
|
||||||
|
#endif // wxUSE_GRAPHICS_CONTEXT
|
||||||
|
dc.SetBackground(*wxBLACK_BRUSH);
|
||||||
|
dc.Clear();
|
||||||
|
dc.SetPen(*wxWHITE_PEN);
|
||||||
|
dc.SetBrush(*wxWHITE_BRUSH);
|
||||||
|
dc.DrawRectangle(4, 4, 8, 8);
|
||||||
|
}
|
||||||
|
REQUIRE_FALSE(color.HasAlpha());
|
||||||
|
REQUIRE(color.GetMask() == NULL);
|
||||||
|
color.SetMask(new wxMask(bmask));
|
||||||
|
REQUIRE_FALSE(color.HasAlpha());
|
||||||
|
REQUIRE(color.GetMask() != NULL);
|
||||||
|
}
|
||||||
|
wxImage image = color.ConvertToImage();
|
||||||
|
|
||||||
|
wxNativePixelData dataColor(color);
|
||||||
|
wxNativePixelData::Iterator rowStartColor(dataColor);
|
||||||
|
wxBitmap mask = color.GetMask()->GetBitmap();
|
||||||
|
wxNativePixelData dataMask(mask);
|
||||||
|
wxNativePixelData::Iterator rowStartMask(dataMask);
|
||||||
|
|
||||||
|
for ( int y = 0; y < color.GetHeight(); ++y ) {
|
||||||
|
wxNativePixelData::Iterator iColor = rowStartColor;
|
||||||
|
wxNativePixelData::Iterator iMask = rowStartMask;
|
||||||
|
for ( int x = 0; x < color.GetWidth(); ++x, ++iColor, ++iMask ) {
|
||||||
|
CHECK((iMask.Red() == 255 && iMask.Green() == 255 && iMask.Blue() == 255 ? iColor.Red() : 1) == image.GetRed(x, y));
|
||||||
|
CHECK((iMask.Red() == 255 && iMask.Green() == 255 && iMask.Blue() == 255 ? iColor.Green() : 2) == image.GetGreen(x, y));
|
||||||
|
CHECK((iMask.Red() == 255 && iMask.Green() == 255 && iMask.Blue() == 255 ? iColor.Blue() : 3) == image.GetBlue(x, y));
|
||||||
|
}
|
||||||
|
rowStartColor.OffsetY(dataColor, 1);
|
||||||
|
rowStartMask.OffsetY(dataMask, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("BitmapTestCase::OverlappingBlit", "[bitmap][blit]")
|
TEST_CASE("BitmapTestCase::OverlappingBlit", "[bitmap][blit]")
|
||||||
{
|
{
|
||||||
wxBitmap bmp(10, 10);
|
wxBitmap bmp(10, 10);
|
||||||
|
Reference in New Issue
Block a user