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) {