sc/inc/document.hxx                          |   10 +++
 sc/inc/table.hxx                             |   10 +++
 sc/qa/unit/tiledrendering/tiledrendering.cxx |    4 +
 sc/source/core/data/document.cxx             |    5 +
 sc/source/core/data/table2.cxx               |   85 +++++++++++++++++++++++++++
 sc/source/ui/view/viewdata.cxx               |   33 +++++++---
 6 files changed, 137 insertions(+), 10 deletions(-)

New commits:
commit 9a42e3db9c644906df801cbc65b71c234fec4f8d
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Fri Dec 20 13:00:39 2024 +0200
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Mon Dec 23 19:41:00 2024 +0100

    optimise ScBoundsProvider::GetIndexTowards
    
    Especially when laying out rows in sheets with thousands of rows.
    By pushing the work down to a new GetRowForHeightPixels method,
    we can do the work in only a couple of iterations of the loop, because row 
heights are normally represented by only a few spans.
    
    Change-Id: If3f62a131a3e7a0794d7352d7c6c1a5de0ef2df0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/179086
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    Tested-by: Jenkins

diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 175843885600..06ae276b3484 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -2059,6 +2059,16 @@ public:
      * specified height.
      */
     SCROW                       GetRowForHeight( SCTAB nTab, tools::Long 
nHeight ) const;
+    /**
+     * Given the height i.e. total vertical distance from the top of the sheet
+     * grid, return the first visible row whose top position is below the
+     * specified height and after the specified row.
+     * Note that this variant uses pixels, not twips.
+     * @param nStartRow the row to start searching at.
+     * @param rStartRowHeightPx this is both the height at nStartRow, and 
returns the height of the first row
+     *        which has height > nHeight
+     */
+    SCROW                       GetRowForHeightPixels( SCTAB nTab, SCROW 
nStartRow, tools::Long& rStartRowHeightPx, tools::Long nHeightPx, double fPPTY 
) const;
     tools::Long                 GetScaledRowHeight( SCROW nStartRow, SCROW 
nEndRow, SCTAB nTab, double fScale ) const;
     SC_DLLPUBLIC tools::Long    GetColOffset( SCCOL nCol, SCTAB nTab, bool 
bHiddenAsZero = true ) const;
     SC_DLLPUBLIC tools::Long    GetRowOffset( SCROW nRow, SCTAB nTab, bool 
bHiddenAsZero = true ) const;
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 5f36e5d9d5c7..74d4f978a523 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -909,6 +909,16 @@ public:
      * @return SCROW last row of the range within specified height.
      */
     SCROW       GetRowForHeight(tools::Long nHeight) const;
+    /**
+     * Given the height i.e. total vertical distance from the top of the sheet
+     * grid, return the first visible row whose top position is below the
+     * specified height.
+     * Note that this variant uses pixels, not twips.
+     * @param nStartRow the row to start searching at.
+     * @param rStartRowHeightPx this is both the height at nStartRow, and 
returns the height of the first row
+     *        which has height > nHeight
+     */
+    SCROW       GetRowForHeightPixels( SCROW nStartRow, tools::Long& 
rStartRowHeightPx, tools::Long nHeightPx, double fPPTY ) const;
 
     sal_uInt16      GetOriginalWidth( SCCOL nCol ) const;
     sal_uInt16      GetOriginalHeight( SCROW nRow ) const;
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index ca4c5191c1c5..8d5280ea3128 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -1547,6 +1547,10 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testInsertDeletePageInvalidation)
 
 CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testGetRowColumnHeadersInvalidation)
 {
+    // NOTE NOTE NOTE
+    // If you run this test in isolation using CPPUNIT_TEST_NAME=, it will 
fail because the invalidations
+    // will be different.
+
     ScModelObj* pModelObj = createDoc("empty.ods");
     ScViewData* pViewData = ScDocShell::GetViewData();
     CPPUNIT_ASSERT(pViewData);
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 36f37a7ee8de..231a42d52a4e 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -4248,6 +4248,11 @@ SCROW ScDocument::GetRowForHeight( SCTAB nTab, 
tools::Long nHeight ) const
     return maTabs[nTab]->GetRowForHeight(nHeight);
 }
 
+SCROW ScDocument::GetRowForHeightPixels( SCTAB nTab, SCROW nStartRow, 
tools::Long& rStartRowHeightPx, tools::Long nHeightPx, double fPPTY ) const
+{
+    return maTabs[nTab]->GetRowForHeightPixels(nStartRow, rStartRowHeightPx, 
nHeightPx, fPPTY);
+}
+
 tools::Long ScDocument::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow,
         SCTAB nTab, double fScale ) const
 {
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 907f08a24cec..4fc0e73397da 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -4326,6 +4326,91 @@ SCROW ScTable::GetRowForHeight(tools::Long nHeight) const
     return -1;
 }
 
+// same as the one in viewdata.hxx
+static tools::Long ToPixel( sal_uInt16 nTwips, double nFactor )
+{
+    tools::Long nRet = static_cast<tools::Long>( nTwips * nFactor );
+    if ( !nRet && nTwips )
+        nRet = 1;
+    return nRet;
+}
+
+SCROW ScTable::GetRowForHeightPixels(SCROW nStartRow, tools::Long& 
rStartRowHeightPx, tools::Long nHeightPx, double fPPTY) const
+{
+    assert(nStartRow >= -1);
+    assert(rStartRowHeightPx >= 0);
+    assert(nHeightPx >= 0);
+
+    // We are iterating over two data arrays here, each of which
+    // is a range/compressed view of the underlying data.
+    tools::Long nSumPx = rStartRowHeightPx;
+
+    ScFlatBoolRowSegments::RangeData aHiddenRange;
+    aHiddenRange.mnRow1 = -1;
+    aHiddenRange.mnRow2 = -1;
+    aHiddenRange.mbValue = false; // silence MSVC C4701
+    ScFlatUInt16RowSegments::RangeData aRowHeightRange;
+    aRowHeightRange.mnRow1 = -1;
+    aRowHeightRange.mnRow2 = -1;
+    aRowHeightRange.mnValue = 1; // silence MSVC C4701
+
+    for (SCROW nRow = nStartRow + 1; nRow <= rDocument.MaxRow(); ++nRow)
+    {
+        // fetch hidden data range if necessary
+        if (aHiddenRange.mnRow2 < nRow)
+        {
+            if (!mpHiddenRows->getRangeData(nRow, aHiddenRange))
+                // Failed to fetch the range data for whatever reason.
+                break;
+        }
+
+        if (aHiddenRange.mbValue)
+        {
+            // This row is hidden.  Skip ahead all hidden rows.
+            nRow = aHiddenRange.mnRow2;
+            continue;
+        }
+
+        // fetch height data range if necessary
+        if (aRowHeightRange.mnRow2 < nRow)
+        {
+            if (!mpRowHeights->getRangeData(nRow, aRowHeightRange))
+                // Failed to fetch the range data for whatever reason.
+                break;
+        }
+
+        assert(aHiddenRange.mnRow1 <= nRow && aHiddenRange.mnRow2 >= nRow && 
"the current hidden-row span should overlap the current row");
+        assert(!aHiddenRange.mbValue && "the current hidden-row span should 
have visible==true");
+        assert(aRowHeightRange.mnRow1 <= nRow && aRowHeightRange.mnRow2 >= 
nRow && "the current height span should overlap the current row");
+
+        // find the last common row between hidden & height spans
+        SCROW nLastCommon = std::min(aHiddenRange.mnRow2, 
aRowHeightRange.mnRow2);
+        SCROW nCommonRows = nLastCommon - nRow + 1;
+        // height of common span
+        tools::Long nRowHeightPx = ToPixel(aRowHeightRange.mnValue, fPPTY);
+        tools::Long nCommonPixels = nRowHeightPx * nCommonRows;
+
+        // is the target height inside the common span ?
+        if (nSumPx + nCommonPixels > nHeightPx)
+        {
+            // calculate how many rows to skip inside the common span
+            SCROW nRowsInside = (nHeightPx - nSumPx) / nRowHeightPx;
+            nRow += nRowsInside;
+
+            assert(aHiddenRange.mnRow1 <= nRow && aHiddenRange.mnRow2 >= nRow 
&& "the current hidden-row span should overlap the current row");
+            assert(aRowHeightRange.mnRow1 <= nRow && aRowHeightRange.mnRow2 >= 
nRow && "the current height span should overlap the current row");
+
+            rStartRowHeightPx = nSumPx + ((nRowsInside + 1) * nRowHeightPx);
+            return nRow;
+        }
+
+        // skip the range and keep hunting
+        nSumPx += nCommonPixels;
+        nRow = nLastCommon;
+    }
+    return -1;
+}
+
 tools::Long ScTable::GetColOffset( SCCOL nCol, bool bHiddenAsZero ) const
 {
     tools::Long n = 0;
diff --git a/sc/source/ui/view/viewdata.cxx b/sc/source/ui/view/viewdata.cxx
index 09e5f834ed44..45c49f294ba4 100644
--- a/sc/source/ui/view/viewdata.cxx
+++ b/sc/source/ui/view/viewdata.cxx
@@ -459,26 +459,39 @@ void ScBoundsProvider::GeIndexBackwards(
 }
 
 void ScBoundsProvider::GetIndexTowards(
-            index_type nNearestIndex, tools::Long nNearestPosition,
-            tools::Long nBound, index_type& nFoundIndex, tools::Long& 
nPosition, bool bTowards)
+            index_type nNearestIndex, tools::Long nNearestPositionPx,
+            tools::Long nBoundPx, index_type& nFoundIndex, tools::Long& 
nPosition, bool bTowards)
 {
     nFoundIndex = -2;
-    for (index_type nIndex = nNearestIndex + 1; nIndex <= MAX_INDEX; ++nIndex)
+    if (bColumnHeader)
     {
-        const tools::Long nSizePx = GetSize(nIndex);
-        nNearestPosition += nSizePx;
+        for (index_type nIndex = nNearestIndex + 1; nIndex <= MAX_INDEX; 
++nIndex)
+        {
+            const sal_uInt16 nSize = rDoc.GetColWidth(nIndex, nTab);
+            const tools::Long nSizePx = ScViewData::ToPixel(nSize, mfPPTX);
+            nNearestPositionPx += nSizePx;
 
-        if (nNearestPosition > nBound)
+            if (nNearestPositionPx > nBoundPx)
+            {
+                nFoundIndex = nIndex; // first index whose nPosition is 
greater than nBoundPx
+                nPosition = nNearestPositionPx;
+                break;
+            }
+        }
+    }
+    else
+    {
+        SCROW nFoundRow = rDoc.GetRowForHeightPixels(nTab, nNearestIndex, 
nNearestPositionPx, nBoundPx, mfPPTY);
+        if (nFoundRow != -1)
         {
-            nFoundIndex = nIndex; // first index whose nPosition is greater 
than nBound
-            nPosition = nNearestPosition;
-            break;
+            nFoundIndex = nFoundRow; // first index whose nPosition is greater 
than nBound
+            nPosition = nNearestPositionPx;
         }
     }
     if (nFoundIndex == -2)
     {
         nFoundIndex = MAX_INDEX;
-        nPosition = nNearestPosition;
+        nPosition = nNearestPositionPx;
     }
     else if (bTowards)
     {

Reply via email to