[Libreoffice-commits] core.git: vcl/qa vcl/source
LuboÅ¡ LuÅák (via logerrit)
logerrit at kemper.freedesktop.org
Mon Feb 22 12:00:44 UTC 2021
vcl/qa/cppunit/BitmapTest.cxx | 65 +++++++++++++++++
vcl/source/bitmap/bitmappaint.cxx | 140 +++++++++++++++++++++++++++++---------
2 files changed, 173 insertions(+), 32 deletions(-)
New commits:
commit e74c9a340ffa34d8d0a7c5bd728148462254a243
Author: Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Fri Feb 19 13:21:52 2021 +0100
Commit: Luboš Luňák <l.lunak at collabora.com>
CommitDate: Mon Feb 22 13:00:03 2021 +0100
optimize Bitmap::Mirror()
For the usual bitmap pixel formats it's much faster to just move
around pixel data rather than call the pixel-set/get functions.
Change-Id: Ie99b3ea1431d965b110ec08d269e163d9f108cf3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111213
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak at collabora.com>
diff --git a/vcl/qa/cppunit/BitmapTest.cxx b/vcl/qa/cppunit/BitmapTest.cxx
index 0c96977a880a..e6c95d27b954 100644
--- a/vcl/qa/cppunit/BitmapTest.cxx
+++ b/vcl/qa/cppunit/BitmapTest.cxx
@@ -45,6 +45,7 @@ class BitmapTest : public CppUnit::TestFixture
void testOctree();
void testEmptyAccess();
void testDitherSize();
+ void testMirror();
CPPUNIT_TEST_SUITE(BitmapTest);
CPPUNIT_TEST(testCreation);
@@ -61,6 +62,7 @@ class BitmapTest : public CppUnit::TestFixture
CPPUNIT_TEST(testOctree);
CPPUNIT_TEST(testEmptyAccess);
CPPUNIT_TEST(testDitherSize);
+ CPPUNIT_TEST(testMirror);
CPPUNIT_TEST_SUITE_END();
};
@@ -682,6 +684,69 @@ void BitmapTest::testDitherSize()
}
}
+void BitmapTest::testMirror()
+{
+ for (int bpp : { 4, 8, 24, 32 })
+ {
+ Bitmap bitmap(Size(11, 11), bpp);
+ {
+ bitmap.Erase(COL_MAGENTA);
+ BitmapWriteAccess write(bitmap);
+ if (write.HasPalette())
+ {
+ // Note that SetPixel() and GetColor() take arguments as Y,X.
+ write.SetPixel(0, 0, BitmapColor(write.GetBestPaletteIndex(COL_BLACK)));
+ write.SetPixel(10, 0, BitmapColor(write.GetBestPaletteIndex(COL_WHITE)));
+ write.SetPixel(0, 10, BitmapColor(write.GetBestPaletteIndex(COL_RED)));
+ write.SetPixel(10, 10, BitmapColor(write.GetBestPaletteIndex(COL_BLUE)));
+ write.SetPixel(5, 0, BitmapColor(write.GetBestPaletteIndex(COL_GREEN)));
+ write.SetPixel(0, 5, BitmapColor(write.GetBestPaletteIndex(COL_YELLOW)));
+ }
+ else
+ {
+ write.SetPixel(0, 0, COL_BLACK);
+ write.SetPixel(10, 0, COL_WHITE);
+ write.SetPixel(0, 10, COL_RED);
+ write.SetPixel(10, 10, COL_BLUE);
+ write.SetPixel(5, 0, COL_GREEN);
+ write.SetPixel(0, 5, COL_YELLOW);
+ }
+ }
+ bitmap.Mirror(BmpMirrorFlags::Horizontal);
+ {
+ BitmapReadAccess read(bitmap);
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLACK), read.GetColor(0, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_WHITE), read.GetColor(10, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_RED), read.GetColor(0, 0));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLUE), read.GetColor(10, 0));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_GREEN), read.GetColor(5, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_YELLOW), read.GetColor(0, 5));
+ }
+ bitmap.Mirror(BmpMirrorFlags::Vertical);
+ {
+ BitmapReadAccess read(bitmap);
+ // Now is effectively mirrored in both directions.
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLACK), read.GetColor(10, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_WHITE), read.GetColor(0, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_RED), read.GetColor(10, 0));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLUE), read.GetColor(0, 0));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_GREEN), read.GetColor(5, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_YELLOW), read.GetColor(10, 5));
+ }
+ bitmap.Mirror(BmpMirrorFlags::Vertical | BmpMirrorFlags::Horizontal);
+ {
+ BitmapReadAccess read(bitmap);
+ // Now is back the original.
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLACK), read.GetColor(0, 0));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_WHITE), read.GetColor(10, 0));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_RED), read.GetColor(0, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLUE), read.GetColor(10, 10));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_GREEN), read.GetColor(5, 0));
+ CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_YELLOW), read.GetColor(0, 5));
+ }
+ }
+}
+
} // namespace
CPPUNIT_TEST_SUITE_REGISTRATION(BitmapTest);
diff --git a/vcl/source/bitmap/bitmappaint.cxx b/vcl/source/bitmap/bitmappaint.cxx
index b69972788746..ad20832022d5 100644
--- a/vcl/source/bitmap/bitmappaint.cxx
+++ b/vcl/source/bitmap/bitmappaint.cxx
@@ -105,6 +105,28 @@ bool Bitmap::Invert()
return bRet;
}
+namespace
+{
+// Put each scanline's content horizontally mirrored into the other one.
+// (optimized version accessing pixel values directly).
+template <int bitCount>
+void mirrorScanlines(Scanline scanline1, Scanline scanline2, tools::Long nWidth)
+{
+ constexpr int byteCount = bitCount / 8;
+ Scanline pos1 = scanline1;
+ Scanline pos2 = scanline2 + (nWidth - 1) * byteCount; // last in second scanline
+ sal_uInt8 tmp[byteCount];
+ for (tools::Long i = 0; i < nWidth; ++i)
+ {
+ memcpy(tmp, pos1, byteCount);
+ memcpy(pos1, pos2, byteCount);
+ memcpy(pos2, tmp, byteCount);
+ pos1 += byteCount;
+ pos2 -= byteCount;
+ }
+}
+}
+
bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
{
bool bHorz(nMirrorFlags & BmpMirrorFlags::Horizontal);
@@ -120,18 +142,40 @@ bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
const tools::Long nWidth = pAcc->Width();
const tools::Long nHeight = pAcc->Height();
const tools::Long nWidth1 = nWidth - 1;
- const tools::Long nWidth_2 = nWidth >> 1;
+ const tools::Long nWidth_2 = nWidth / 2;
+ const tools::Long nSecondHalf = nWidth - nWidth_2;
- for (tools::Long nY = 0; nY < nHeight; nY++)
+ switch (pAcc->GetBitCount())
{
- Scanline pScanline = pAcc->GetScanline(nY);
- for (tools::Long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--)
- {
- const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
+ // Special-case these, swap the halves of scanlines while mirroring them.
+ case 32:
+ for (tools::Long nY = 0; nY < nHeight; nY++)
+ mirrorScanlines<32>(pAcc->GetScanline(nY),
+ pAcc->GetScanline(nY) + 4 * nSecondHalf, nWidth_2);
+ break;
+ case 24:
+ for (tools::Long nY = 0; nY < nHeight; nY++)
+ mirrorScanlines<24>(pAcc->GetScanline(nY),
+ pAcc->GetScanline(nY) + 3 * nSecondHalf, nWidth_2);
+ break;
+ case 8:
+ for (tools::Long nY = 0; nY < nHeight; nY++)
+ mirrorScanlines<8>(pAcc->GetScanline(nY),
+ pAcc->GetScanline(nY) + nSecondHalf, nWidth_2);
+ break;
+ default:
+ for (tools::Long nY = 0; nY < nHeight; nY++)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ for (tools::Long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--)
+ {
+ const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
- pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOther));
- pAcc->SetPixelOnData(pScanline, nOther, aTemp);
- }
+ pAcc->SetPixelOnData(pScanline, nX,
+ pAcc->GetPixelFromData(pScanline, nOther));
+ pAcc->SetPixelOnData(pScanline, nOther, aTemp);
+ }
+ }
}
pAcc.reset();
@@ -170,33 +214,65 @@ bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags)
const tools::Long nWidth = pAcc->Width();
const tools::Long nWidth1 = nWidth - 1;
const tools::Long nHeight = pAcc->Height();
- tools::Long nHeight_2 = nHeight >> 1;
+ tools::Long nHeight_2 = nHeight / 2;
+ const tools::Long nWidth_2 = nWidth / 2;
+ const tools::Long nSecondHalf = nWidth - nWidth_2;
- for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
+ switch (pAcc->GetBitCount())
{
- Scanline pScanline = pAcc->GetScanline(nY);
- Scanline pScanlineOther = pAcc->GetScanline(nOtherY);
- for (tools::Long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--)
- {
- const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
+ case 32:
+ for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
+ mirrorScanlines<32>(pAcc->GetScanline(nY), pAcc->GetScanline(nOtherY),
+ nWidth);
+ if (nHeight & 1)
+ mirrorScanlines<32>(pAcc->GetScanline(nHeight_2),
+ pAcc->GetScanline(nHeight_2) + 4 * nSecondHalf,
+ nWidth_2);
+ break;
+ case 24:
+ for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
+ mirrorScanlines<24>(pAcc->GetScanline(nY), pAcc->GetScanline(nOtherY),
+ nWidth);
+ if (nHeight & 1)
+ mirrorScanlines<24>(pAcc->GetScanline(nHeight_2),
+ pAcc->GetScanline(nHeight_2) + 3 * nSecondHalf,
+ nWidth_2);
+ break;
+ case 8:
+ for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
+ mirrorScanlines<8>(pAcc->GetScanline(nY), pAcc->GetScanline(nOtherY),
+ nWidth);
+ if (nHeight & 1)
+ mirrorScanlines<8>(pAcc->GetScanline(nHeight_2),
+ pAcc->GetScanline(nHeight_2) + nSecondHalf, nWidth_2);
+ break;
+ default:
+ for (tools::Long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--)
+ {
+ Scanline pScanline = pAcc->GetScanline(nY);
+ Scanline pScanlineOther = pAcc->GetScanline(nOtherY);
+ for (tools::Long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--)
+ {
+ const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
- pAcc->SetPixelOnData(pScanline, nX,
- pAcc->GetPixelFromData(pScanlineOther, nOtherX));
- pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp);
- }
- }
+ pAcc->SetPixelOnData(pScanline, nX,
+ pAcc->GetPixelFromData(pScanlineOther, nOtherX));
+ pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp);
+ }
+ }
- // if necessary, also mirror the middle line horizontally
- if (nHeight & 1)
- {
- Scanline pScanline = pAcc->GetScanline(nHeight_2);
- for (tools::Long nX = 0, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2;
- nX++, nOtherX--)
- {
- const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
- pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOtherX));
- pAcc->SetPixelOnData(pScanline, nOtherX, aTemp);
- }
+ // if necessary, also mirror the middle line horizontally
+ if (nHeight & 1)
+ {
+ Scanline pScanline = pAcc->GetScanline(nHeight_2);
+ for (tools::Long nX = 0, nOtherX = nWidth1; nX < nWidth_2; nX++, nOtherX--)
+ {
+ const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX));
+ pAcc->SetPixelOnData(pScanline, nX,
+ pAcc->GetPixelFromData(pScanline, nOtherX));
+ pAcc->SetPixelOnData(pScanline, nOtherX, aTemp);
+ }
+ }
}
pAcc.reset();
More information about the Libreoffice-commits
mailing list