vcl/qa/cppunit/BitmapFilterTest.cxx         |    4 
 vcl/source/bitmap/BitmapFilterStackBlur.cxx |  189 ++++++++--------------------
 2 files changed, 57 insertions(+), 136 deletions(-)

New commits:
commit 2ef907868db1b57e64ac7f9cda1396b6f39b0fe9
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Sat Feb 21 13:39:27 2026 +0900
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Sun Feb 22 16:20:47 2026 +0100

    vcl: simplify 2 horiz. and vert. functions into 1 in stack blur impl.
    
    Makes the code simpler and should still perform the same.
    
    Change-Id: I17854eaf06770933fdd6122863aa5415bc52c7a3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199920
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/vcl/qa/cppunit/BitmapFilterTest.cxx 
b/vcl/qa/cppunit/BitmapFilterTest.cxx
index 4c48d25fd69f..06032dbcea40 100644
--- a/vcl/qa/cppunit/BitmapFilterTest.cxx
+++ b/vcl/qa/cppunit/BitmapFilterTest.cxx
@@ -220,14 +220,14 @@ void BitmapFilterTest::testPerformance()
     }
 
     int nIterations = 10;
-    auto start = std::chrono::high_resolution_clock::now();
+    auto start = std::chrono::steady_clock::now();
     Bitmap aResult;
     for (int i = 0; i < nIterations; i++)
     {
         BitmapFilterStackBlur aBlurFilter(250);
         aResult = aBlurFilter.execute(aBigBitmap);
     }
-    auto end = std::chrono::high_resolution_clock::now();
+    auto end = std::chrono::steady_clock::now();
     auto elapsed = (end - start) / nIterations;
 
     if (constWriteResultBitmap)
diff --git a/vcl/source/bitmap/BitmapFilterStackBlur.cxx 
b/vcl/source/bitmap/BitmapFilterStackBlur.cxx
index 1db2cb9768f9..427bc47b3fba 100644
--- a/vcl/source/bitmap/BitmapFilterStackBlur.cxx
+++ b/vcl/source/bitmap/BitmapFilterStackBlur.cxx
@@ -66,6 +66,22 @@ struct BlurSharedData
         , mnColorChannels(nColorChannels)
     {
     }
+
+    template <bool Horizontal> Scanline readPixel(sal_Int32 nOuter, sal_Int32 
nInner) const
+    {
+        if constexpr (Horizontal)
+            return mpReadAccess->GetScanline(nOuter) + mnComponentWidth * 
nInner;
+        else
+            return mpReadAccess->GetScanline(nInner) + mnComponentWidth * 
nOuter;
+    }
+
+    template <bool Horizontal> Scanline writePixel(sal_Int32 nOuter, sal_Int32 
nInner) const
+    {
+        if constexpr (Horizontal)
+            return mpWriteAccess->GetScanline(nOuter) + mnComponentWidth * 
nInner;
+        else
+            return mpWriteAccess->GetScanline(nInner) + mnComponentWidth * 
nOuter;
+    }
 };
 
 struct BlurArrays
@@ -236,19 +252,17 @@ struct SumFunction8
     }
 };
 
-template <typename SumFunction>
-void stackBlurHorizontal(BlurSharedData const& rShared, sal_Int32 nStart, 
sal_Int32 nEnd)
+template <typename SumFunction, bool Horizontal>
+void stackBlur(BlurSharedData const& rShared, sal_Int32 nStart, sal_Int32 nEnd)
 {
-    BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
-    BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
-
     BlurArrays aArrays(rShared);
 
     sal_uInt8* pStack = aArrays.maStackBuffer.data();
     sal_uInt8* pStackPtr;
 
-    const sal_Int32 nWidth = pReadAccess->Width();
-    const sal_Int32 nLastIndexX = nWidth - 1;
+    const sal_Int32 nLength
+        = Horizontal ? rShared.mpReadAccess->Width() : 
rShared.mpReadAccess->Height();
+    const sal_Int32 nLastIndex = nLength - 1;
 
     const sal_Int32 nMultiplyValue = aArrays.getMultiplyValue();
     const sal_Int32 nShiftValue = aArrays.getShiftValue();
@@ -260,7 +274,7 @@ void stackBlurHorizontal(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_In
     Scanline pSourcePointer;
     Scanline pDestinationPointer;
 
-    aArrays.initializeWeightAndPositions(nLastIndexX);
+    aArrays.initializeWeightAndPositions(nLastIndex);
 
     sal_Int32* nSum = aArrays.mnSumVector.data();
     sal_Int32* nInSum = aArrays.mnInSumVector.data();
@@ -269,7 +283,7 @@ void stackBlurHorizontal(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_In
     sal_Int32* pPositionPointer = aArrays.maPositionTable.data();
     sal_Int32* pWeightPointer = aArrays.maWeightTable.data();
 
-    for (sal_Int32 y = nStart; y <= nEnd; y++)
+    for (sal_Int32 nOuter = nStart; nOuter <= nEnd; nOuter++)
     {
         SumFunction::set(nSum, 0);
         SumFunction::set(nInSum, 0);
@@ -277,13 +291,13 @@ void stackBlurHorizontal(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_In
 
         // Pre-initialize blur data for first pixel.
         // aArrays.maPositionTable contains values like (for radius of 5): 
[0,0,0,0,0,0,1,2,3,4,5],
-        // which are used as pixels indices in the current row that we use to 
prepare information
-        // for the first pixel; aArrays.maWeightTable has 
[1,2,3,4,5,6,5,4,3,2,1]. Before looking at
-        // the first row pixel, we pretend to have processed fake previous 
pixels, as if the row was
-        // extended to the left with the same color as that of the first pixel.
+        // which are used as pixel indices along the blur direction that we 
use to prepare
+        // information for the first pixel; aArrays.maWeightTable has 
[1,2,3,4,5,6,5,4,3,2,1].
+        // Before looking at the first pixel, we pretend to have processed 
fake previous pixels,
+        // as if the line was extended with the same color as that of the 
first pixel.
         for (sal_Int32 i = 0; i < nDiv; i++)
         {
-            pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * 
pPositionPointer[i];
+            pSourcePointer = rShared.readPixel<Horizontal>(nOuter, 
pPositionPointer[i]);
 
             pStackPtr = &pStack[nComponentWidth * i];
 
@@ -302,13 +316,13 @@ void stackBlurHorizontal(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_In
         }
 
         sal_Int32 nStackIndex = nRadius;
-        sal_Int32 nXPosition = std::min(nRadius, nLastIndexX);
+        sal_Int32 nPosition = std::min(nRadius, nLastIndex);
 
-        pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth * 
nXPosition;
+        pSourcePointer = rShared.readPixel<Horizontal>(nOuter, nPosition);
 
-        for (sal_Int32 x = 0; x < nWidth; x++)
+        for (sal_Int32 nInner = 0; nInner < nLength; nInner++)
         {
-            pDestinationPointer = pWriteAccess->GetScanline(y) + 
nComponentWidth * x;
+            pDestinationPointer = rShared.writePixel<Horizontal>(nOuter, 
nInner);
 
             SumFunction::assignMulAndShr(pDestinationPointer, nSum, 
nMultiplyValue, nShiftValue);
 
@@ -323,10 +337,10 @@ void stackBlurHorizontal(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_In
 
             SumFunction::sub(nOutSum, pStackPtr);
 
-            if (nXPosition < nLastIndexX)
+            if (nPosition < nLastIndex)
             {
-                nXPosition++;
-                pSourcePointer = pReadAccess->GetScanline(y) + nComponentWidth 
* nXPosition;
+                nPosition++;
+                pSourcePointer = rShared.readPixel<Horizontal>(nOuter, 
nPosition);
             }
 
             SumFunction::assignPtr(pStackPtr, pSourcePointer);
@@ -350,111 +364,15 @@ void stackBlurHorizontal(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_In
 }
 
 template <typename SumFunction>
-void stackBlurVertical(BlurSharedData const& rShared, sal_Int32 nStart, 
sal_Int32 nEnd)
+void stackBlurHorizontal(BlurSharedData const& rShared, sal_Int32 nStart, 
sal_Int32 nEnd)
 {
-    BitmapReadAccess* pReadAccess = rShared.mpReadAccess;
-    BitmapWriteAccess* pWriteAccess = rShared.mpWriteAccess;
-
-    BlurArrays aArrays(rShared);
-
-    sal_uInt8* pStack = aArrays.maStackBuffer.data();
-    sal_uInt8* pStackPtr;
-
-    sal_Int32 nHeight = pReadAccess->Height();
-    sal_Int32 nLastIndexY = nHeight - 1;
-
-    sal_Int32 nMultiplyValue = aArrays.getMultiplyValue();
-    sal_Int32 nShiftValue = aArrays.getShiftValue();
-
-    sal_Int32 nRadius = rShared.mnRadius;
-    sal_Int32 nComponentWidth = rShared.mnComponentWidth;
-    sal_Int32 nDiv = rShared.mnDiv;
-
-    Scanline pSourcePointer;
-    Scanline pDestinationPointer;
-
-    aArrays.initializeWeightAndPositions(nLastIndexY);
-
-    sal_Int32* nSum = aArrays.mnSumVector.data();
-    sal_Int32* nInSum = aArrays.mnInSumVector.data();
-    sal_Int32* nOutSum = aArrays.mnOutSumVector.data();
-    sal_Int32* pPositionPointer = aArrays.maPositionTable.data();
-    sal_Int32* pWeightPointer = aArrays.maWeightTable.data();
-
-    for (sal_Int32 x = nStart; x <= nEnd; x++)
-    {
-        SumFunction::set(nSum, 0);
-        SumFunction::set(nInSum, 0);
-        SumFunction::set(nOutSum, 0);
-
-        // Pre-initialize blur data for first pixel.
-        // aArrays.maPositionTable contains values like (for radius of 5): 
[0,0,0,0,0,0,1,2,3,4,5],
-        // which are used as pixels indices in the current column that we use 
to prepare information
-        // for the first pixel; aArrays.maWeightTable has 
[1,2,3,4,5,6,5,4,3,2,1]. Before looking at
-        // the first column pixels, we pretend to have processed fake previous 
pixels, as if the
-        // column was extended to the top with the same color as that of the 
first pixel.
-        for (sal_Int32 i = 0; i < nDiv; i++)
-        {
-            pSourcePointer = pReadAccess->GetScanline(pPositionPointer[i]) + 
nComponentWidth * x;
-
-            pStackPtr = &pStack[nComponentWidth * i];
-
-            SumFunction::assignPtr(pStackPtr, pSourcePointer);
-
-            SumFunction::mulAndAdd(nSum, pSourcePointer, pWeightPointer[i]);
-
-            if (i - nRadius > 0)
-            {
-                SumFunction::add(nInSum, pSourcePointer);
-            }
-            else
-            {
-                SumFunction::add(nOutSum, pSourcePointer);
-            }
-        }
-
-        sal_Int32 nStackIndex = nRadius;
-        sal_Int32 nYPosition = std::min(nRadius, nLastIndexY);
-
-        pSourcePointer = pReadAccess->GetScanline(nYPosition) + 
nComponentWidth * x;
-
-        for (sal_Int32 y = 0; y < nHeight; y++)
-        {
-            pDestinationPointer = pWriteAccess->GetScanline(y) + 
nComponentWidth * x;
-
-            SumFunction::assignMulAndShr(pDestinationPointer, nSum, 
nMultiplyValue, nShiftValue);
-
-            SumFunction::sub(nSum, nOutSum);
-
-            sal_Int32 nStackIndexStart = nStackIndex + nDiv - nRadius;
-
-            if (nStackIndexStart >= nDiv)
-                nStackIndexStart -= nDiv;
-
-            pStackPtr = &pStack[nComponentWidth * nStackIndexStart];
-
-            SumFunction::sub(nOutSum, pStackPtr);
-
-            if (nYPosition < nLastIndexY)
-            {
-                nYPosition++;
-                pSourcePointer = pReadAccess->GetScanline(nYPosition) + 
nComponentWidth * x;
-            }
-
-            SumFunction::assignPtr(pStackPtr, pSourcePointer);
-            SumFunction::add(nInSum, pSourcePointer);
-            SumFunction::add(nSum, nInSum);
-
-            nStackIndex++;
-            if (nStackIndex >= nDiv)
-                nStackIndex = 0;
-
-            pStackPtr = &pStack[nStackIndex * nComponentWidth];
+    stackBlur<SumFunction, true>(rShared, nStart, nEnd);
+}
 
-            SumFunction::add(nOutSum, pStackPtr);
-            SumFunction::sub(nInSum, pStackPtr);
-        }
-    }
+template <typename SumFunction>
+void stackBlurVertical(BlurSharedData const& rShared, sal_Int32 nStart, 
sal_Int32 nEnd)
+{
+    stackBlur<SumFunction, false>(rShared, nStart, nEnd);
 }
 
 constexpr sal_Int32 nThreadStrip = 16;
commit b90d690da8086ec91b279ec6987a231b0775d24e
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Sat Feb 21 12:48:29 2026 +0900
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Sun Feb 22 16:20:35 2026 +0100

    sc: Fix bug in stack blur algorithm
    
    In RGB case we only multiplied the first component instead of all.
    This introduces mulAndAdd function to multiply each component with
    the constant and adds to the array for each component. This should
    fix the issue. The add() function that takes a constant was
    removed.
    
    Change-Id: I7e2620f643b49498a69437e4b572f9417f1ef6af
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199919
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/vcl/source/bitmap/BitmapFilterStackBlur.cxx 
b/vcl/source/bitmap/BitmapFilterStackBlur.cxx
index 0f8c2b602bbd..1db2cb9768f9 100644
--- a/vcl/source/bitmap/BitmapFilterStackBlur.cxx
+++ b/vcl/source/bitmap/BitmapFilterStackBlur.cxx
@@ -137,13 +137,6 @@ public:
 
 struct SumFunction24
 {
-    static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant)
-    {
-        pValue1[0] += nConstant;
-        pValue1[1] += nConstant;
-        pValue1[2] += nConstant;
-    }
-
     static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant)
     {
         pValue1[0] = nConstant;
@@ -151,6 +144,13 @@ struct SumFunction24
         pValue1[2] = nConstant;
     }
 
+    static inline void mulAndAdd(sal_Int32*& pValue1, const sal_uInt8* 
pValue2, sal_Int32 nConstant)
+    {
+        pValue1[0] += pValue2[0] * nConstant;
+        pValue1[1] += pValue2[1] * nConstant;
+        pValue1[2] += pValue2[2] * nConstant;
+    }
+
     static inline void add(sal_Int32*& pValue1, const sal_uInt8* pValue2)
     {
         pValue1[0] += pValue2[0];
@@ -197,10 +197,13 @@ struct SumFunction24
 
 struct SumFunction8
 {
-    static inline void add(sal_Int32*& pValue1, sal_Int32 nConstant) { 
pValue1[0] += nConstant; }
-
     static inline void set(sal_Int32*& pValue1, sal_Int32 nConstant) { 
pValue1[0] = nConstant; }
 
+    static inline void mulAndAdd(sal_Int32*& pValue1, const sal_uInt8* 
pValue2, sal_Int32 nConstant)
+    {
+        pValue1[0] += pValue2[0] * nConstant;
+    }
+
     static inline void add(sal_Int32*& pValue1, const sal_uInt8* pValue2)
     {
         pValue1[0] += pValue2[0];
@@ -286,7 +289,7 @@ void stackBlurHorizontal(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_In
 
             SumFunction::assignPtr(pStackPtr, pSourcePointer);
 
-            SumFunction::add(nSum, pSourcePointer[0] * pWeightPointer[i]);
+            SumFunction::mulAndAdd(nSum, pSourcePointer, pWeightPointer[i]);
 
             if (i - nRadius > 0)
             {
@@ -398,7 +401,7 @@ void stackBlurVertical(BlurSharedData const& rShared, 
sal_Int32 nStart, sal_Int3
 
             SumFunction::assignPtr(pStackPtr, pSourcePointer);
 
-            SumFunction::add(nSum, pSourcePointer[0] * pWeightPointer[i]);
+            SumFunction::mulAndAdd(nSum, pSourcePointer, pWeightPointer[i]);
 
             if (i - nRadius > 0)
             {

Reply via email to