diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2021-02-19 13:21:52 +0100 |
---|---|---|
committer | Andras Timar <andras.timar@collabora.com> | 2022-02-03 21:33:05 +0100 |
commit | f2ea6902b4f901b24e3f551c95280123cff662ab (patch) | |
tree | 4bd79bf06584bd91781be8ccd306e22d91847046 | |
parent | 4beba67510fd4a25b8c5d31e0a330d577c8f9762 (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.cxx | 65 | ||||
-rw-r--r-- | vcl/source/bitmap/bitmappaint.cxx | 140 |
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(); |