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 90dd60beffe1385ae0f4fffea60e386eef40f5db Author: Noel Grandin <noel.gran...@collabora.co.uk> AuthorDate: Fri Dec 20 13:00:39 2024 +0200 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Mon Jan 20 07:32:25 2025 +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/+/179174 Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Michael Meeks <michael.me...@collabora.com> diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 69cd77c0a90c..b8883f958af9 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1991,6 +1991,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 fef0247b4335..d25d67a8e735 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -908,6 +908,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 e1699f13bd3d..d8faaca18469 100644 --- a/sc/qa/unit/tiledrendering/tiledrendering.cxx +++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx @@ -2008,6 +2008,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 14e0e04568d9..2d932f87f039 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -4235,6 +4235,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 db31b75e3bf9..643eb6b04384 100644 --- a/sc/source/core/data/table2.cxx +++ b/sc/source/core/data/table2.cxx @@ -4338,6 +4338,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 4a4021b74ff7..3499eed62f35 100644 --- a/sc/source/ui/view/viewdata.cxx +++ b/sc/source/ui/view/viewdata.cxx @@ -460,26 +460,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) {