[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