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.lu...@collabora.com> AuthorDate: Fri Feb 19 13:21:52 2021 +0100 Commit: Luboš Luňák <l.lu...@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.lu...@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(); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits