summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2021-02-19 13:21:52 +0100
committerAndras Timar <andras.timar@collabora.com>2022-02-03 21:33:05 +0100
commitf2ea6902b4f901b24e3f551c95280123cff662ab (patch)
tree4bd79bf06584bd91781be8ccd306e22d91847046
parent4beba67510fd4a25b8c5d31e0a330d577c8f9762 (diff)
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@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129411 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Andras Timar <andras.timar@collabora.com>
-rw-r--r--vcl/qa/cppunit/BitmapTest.cxx65
-rw-r--r--vcl/source/bitmap/bitmappaint.cxx140
2 files changed, 173 insertions, 32 deletions
diff --git a/vcl/qa/cppunit/BitmapTest.cxx b/vcl/qa/cppunit/BitmapTest.cxx
index 490a2d3ce8c7..f2c61e67b770 100644
--- a/vcl/qa/cppunit/BitmapTest.cxx
+++ b/vcl/qa/cppunit/BitmapTest.cxx
@@ -49,6 +49,7 @@ class BitmapTest : public CppUnit::TestFixture
void testBitmap32();
void testOctree();
void testEmptyAccess();
+ void testMirror();
CPPUNIT_TEST_SUITE(BitmapTest);
CPPUNIT_TEST(testCreation);
@@ -64,6 +65,7 @@ class BitmapTest : public CppUnit::TestFixture
CPPUNIT_TEST(testBitmap32);
CPPUNIT_TEST(testOctree);
CPPUNIT_TEST(testEmptyAccess);
+ CPPUNIT_TEST(testMirror);
CPPUNIT_TEST_SUITE_END();
};
@@ -661,6 +663,69 @@ void BitmapTest::testEmptyAccess()
CPPUNIT_ASSERT_EQUAL(tools::Long(0), access.Height());
}
+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 75188dd184ad..c5ad78d7a945 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();