Add real support for monochrome bitmaps to wxMSW

In order to be able to use monochrome bitmaps as wxMask, improve support
for them in various ways:

1. Implement loading and saving of monochrome BMP files.
2. Add wxMonoPixelData for direct access to monochrome bitmap pixels.
3. Implement conversion from wxImage to monochrome wxBitmap.

Closes https://github.com/wxWidgets/wxWidgets/pull/2032
This commit is contained in:
Bill Su
2020-10-19 20:50:31 -04:00
committed by Vadim Zeitlin
parent c0e3bdaab9
commit 7e9afad53a
5 changed files with 602 additions and 69 deletions

View File

@@ -22,6 +22,7 @@
#include "wx/graphics.h"
#endif // wxUSE_GRAPHICS_CONTEXT
#include "testfile.h"
#include "testimage.h"
#define ASSERT_EQUAL_RGB(c, r, g, b) \
@@ -54,6 +55,52 @@ typedef wxNativePixelData wxNative32PixelData;
// tests
// ----------------------------------------------------------------------------
TEST_CASE("BitmapTestCase::Monochrome", "[bitmap][monochrome]")
{
#ifdef __WXGTK__
WARN("Skipping test known not to work in wxGTK.");
#else
wxBitmap color;
color.LoadFile("horse.bmp", wxBITMAP_TYPE_BMP);
REQUIRE(color.IsOk());
REQUIRE(color.GetDepth() == 32);
wxImage imgQuant = color.ConvertToImage();
wxBitmap bmpQuant(imgQuant, 1);
REQUIRE(bmpQuant.GetDepth() == 1);
TempFile mono_horse("mono_horse.bmp");
REQUIRE(bmpQuant.SaveFile(mono_horse.GetName(), wxBITMAP_TYPE_BMP));
wxBitmap mono;
REQUIRE(mono.LoadFile(mono_horse.GetName(), wxBITMAP_TYPE_BMP));
REQUIRE(mono.IsOk());
REQUIRE(mono.GetDepth() == 1);
// wxMonoPixelData only exists in wxMSW
#if defined(__WXMSW__)
// draw lines on top and left, but leaving blank top and left lines
{
wxMonoPixelData data(mono);
wxMonoPixelData::Iterator p(data);
p.OffsetY(data, 1);
for ( int i = 0; i < data.GetWidth() - 2; ++i )
{
++p;
p.Pixel() = 0;
}
p.MoveTo(data, 1, 1);
for ( int i = 0; i < data.GetHeight() - 3; ++i )
{
p.OffsetY(data, 1);
p.Pixel() = 1;
}
}
TempFile mono_lines_horse("mono_lines_horse.bmp");
REQUIRE(mono.SaveFile(mono_lines_horse.GetName(), wxBITMAP_TYPE_BMP));
#endif // __WXMSW__
#endif // !__WXGTK__
}
TEST_CASE("BitmapTestCase::Mask", "[bitmap][mask]")
{
wxBitmap bmp(10, 10);
@@ -726,6 +773,7 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona
wxColour maskClrBottomRight;
// Fetch sample original mask pixels
{
REQUIRE(bmpMask.GetDepth() == 1);
wxNativePixelData data(bmpMask);
REQUIRE(data);
wxNativePixelData::Iterator p(data);
@@ -742,11 +790,44 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona
p.OffsetX(data, w / 2); // bottom-right point
maskClrBottomRight = wxColour(p.Red(), p.Green(), p.Blue());
}
REQUIRE(bmpMask.GetDepth() == 1);
CHECK(maskClrTopLeft == *wxWHITE);
CHECK(maskClrTopRight == *wxWHITE);
CHECK(maskClrBottomLeft == *wxBLACK);
CHECK(maskClrBottomRight == *wxBLACK);
// wxMonoPixelData only exists in wxMSW
#if defined(__WXMSW__)
bool maskValueTopLeft;
bool maskValueTopRight;
bool maskValueBottomLeft;
bool maskValueBottomRight;
// Fetch sample original mask pixels
{
REQUIRE(bmpMask.GetDepth() == 1);
wxMonoPixelData data(bmpMask);
REQUIRE(data);
wxMonoPixelData::Iterator p(data);
p.OffsetY(data, h / 4);
wxMonoPixelData::Iterator rowStart = p;
p.OffsetX(data, w / 4); // top-left point
maskValueTopLeft = p.Pixel();
p.OffsetX(data, w / 2); // top-right point
maskValueTopRight = p.Pixel();
p = rowStart;
p.OffsetY(data, h / 2);
p.OffsetX(data, w / 4); // bottom-left point
maskValueBottomLeft = p.Pixel();
p.OffsetX(data, w / 2); // bottom-right point
maskValueBottomRight = p.Pixel();
}
REQUIRE(bmpMask.GetDepth() == 1);
CHECK(maskValueTopLeft == true);
CHECK(maskValueTopRight == true);
CHECK(maskValueBottomLeft == false);
CHECK(maskValueBottomRight == false);
#endif // __WXMSW__
wxBitmap subBmpMask = subBmp.GetMask()->GetBitmap();
// Check sub bitmap mask attributes
REQUIRE(subBmpMask.GetWidth() == subBmp.GetWidth());
@@ -758,6 +839,7 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona
REQUIRE_FALSE(subBmpMask.GetMask());
// Check sub bitmap mask pixels
{
REQUIRE(subBmpMask.GetDepth() == 1);
wxNativePixelData data(subBmpMask);
REQUIRE(data);
wxNativePixelData::Iterator p(data);
@@ -774,6 +856,30 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona
p.OffsetX(data, w2 / 2); // bottom-right point
ASSERT_EQUAL_COLOUR_RGB(p, maskClrBottomRight);
}
REQUIRE(subBmpMask.GetDepth() == 1);
// wxMonoPixelData only exists in wxMSW
#if defined(__WXMSW__)
{
REQUIRE(subBmpMask.GetDepth() == 1);
wxMonoPixelData data(subBmpMask);
REQUIRE(data);
wxMonoPixelData::Iterator p(data);
p.OffsetY(data, h2 / 4);
wxMonoPixelData::Iterator rowStart = p;
p.OffsetX(data, w2 / 4); // top-left point
CHECK(p.Pixel() == maskValueTopLeft);
p.OffsetX(data, w2 / 2); // top-right point
CHECK(p.Pixel() == maskValueTopRight);
p = rowStart;
p.OffsetY(data, h2 / 2);
p.OffsetX(data, w2 / 4); // bottom-left point
CHECK(p.Pixel() == maskValueBottomLeft);
p.OffsetX(data, w2 / 2); // bottom-right point
CHECK(p.Pixel() == maskValueBottomRight);
}
REQUIRE(subBmpMask.GetDepth() == 1);
#endif // __WXMSW__
}
TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][withmask]")
@@ -870,6 +976,7 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][
wxColour maskClrBottomRight;
// Fetch sample original mask pixels
{
REQUIRE(bmpMask.GetDepth() == 1);
wxNativePixelData data(bmpMask);
REQUIRE(data);
wxNativePixelData::Iterator p(data);
@@ -886,11 +993,44 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][
p.OffsetX(data, w / 2); // bottom-right point
maskClrBottomRight = wxColour(p.Red(), p.Green(), p.Blue());
}
REQUIRE(bmpMask.GetDepth() == 1);
CHECK(maskClrTopLeft == *wxWHITE);
CHECK(maskClrTopRight == *wxWHITE);
CHECK(maskClrBottomLeft == *wxBLACK);
CHECK(maskClrBottomRight == *wxBLACK);
// wxMonoPixelData only exists in wxMSW
#if defined(__WXMSW__)
bool maskValueTopLeft;
bool maskValueTopRight;
bool maskValueBottomLeft;
bool maskValueBottomRight;
// Fetch sample original mask pixels
{
REQUIRE(bmpMask.GetDepth() == 1);
wxMonoPixelData data(bmpMask);
REQUIRE(data);
wxMonoPixelData::Iterator p(data);
p.OffsetY(data, h / 4);
wxMonoPixelData::Iterator rowStart = p;
p.OffsetX(data, w / 4); // top-left point
maskValueTopLeft = p.Pixel();
p.OffsetX(data, w / 2); // top-right point
maskValueTopRight = p.Pixel();
p = rowStart;
p.OffsetY(data, h / 2);
p.OffsetX(data, w / 4); // bottom-left point
maskValueBottomLeft = p.Pixel();
p.OffsetX(data, w / 2); // bottom-right point
maskValueBottomRight = p.Pixel();
}
REQUIRE(bmpMask.GetDepth() == 1);
CHECK(maskValueTopLeft == true);
CHECK(maskValueTopRight == true);
CHECK(maskValueBottomLeft == false);
CHECK(maskValueBottomRight == false);
#endif // __WXMSW__
wxBitmap subBmpMask = subBmp.GetMask()->GetBitmap();
// Check sub bitmap mask attributes
REQUIRE(subBmpMask.GetWidth() == subBmp.GetWidth());
@@ -902,6 +1042,7 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][
REQUIRE_FALSE(subBmpMask.GetMask());
// Check sub bitmap mask pixels
{
REQUIRE(subBmpMask.GetDepth() == 1);
wxNativePixelData data(subBmpMask);
REQUIRE(data);
wxNativePixelData::Iterator p(data);
@@ -918,6 +1059,30 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][
p.OffsetX(data, w2 / 2); // bottom-right point
ASSERT_EQUAL_RGB(p, maskClrBottomRight.Red(), maskClrBottomRight.Green(), maskClrBottomRight.Blue());
}
REQUIRE(subBmpMask.GetDepth() == 1);
// wxMonoPixelData only exists in wxMSW
#if defined(__WXMSW__)
{
REQUIRE(subBmpMask.GetDepth() == 1);
wxMonoPixelData data(subBmpMask);
REQUIRE(data);
wxMonoPixelData::Iterator p(data);
p.OffsetY(data, h2 / 4);
wxMonoPixelData::Iterator rowStart = p;
p.OffsetX(data, w2 / 4); // top-left point
CHECK(p.Pixel() == maskValueTopLeft);
p.OffsetX(data, w2 / 2); // top-right point
CHECK(p.Pixel() == maskValueTopRight);
p = rowStart;
p.OffsetY(data, h2 / 2);
p.OffsetX(data, w2 / 4); // bottom-left point
CHECK(p.Pixel() == maskValueBottomLeft);
p.OffsetX(data, w2 / 2); // bottom-right point
CHECK(p.Pixel() == maskValueBottomRight);
}
REQUIRE(subBmpMask.GetDepth() == 1);
#endif // __WXMSW__
}
namespace Catch