include/oox/vml/vmlshapecontainer.hxx | 5 ++++- sc/source/filter/inc/commentsbuffer.hxx | 3 ++- sc/source/filter/inc/drawingfragment.hxx | 8 ++++++++ sc/source/filter/oox/commentsbuffer.cxx | 11 ++++++++--- sc/source/filter/oox/drawingfragment.cxx | 20 ++++++++++++++++++++ 5 files changed, 42 insertions(+), 5 deletions(-)
New commits: commit b3df12b9781daa19eff2b1e30526e65ca2aaa193 Author: Noel Grandin <noel.gran...@collabora.co.uk> AuthorDate: Thu May 22 11:09:10 2025 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Sun May 25 12:31:17 2025 +0200 tdf#166684 avoid O(n^2) loop when importing comments shaves 5% off load time Change-Id: I5778b619b859c4090f6d35b1608032148f81795c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185652 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/include/oox/vml/vmlshapecontainer.hxx b/include/oox/vml/vmlshapecontainer.hxx index 6f50730bcacc..5f8dc70ba97e 100644 --- a/include/oox/vml/vmlshapecontainer.hxx +++ b/include/oox/vml/vmlshapecontainer.hxx @@ -53,6 +53,8 @@ struct ShapeParentAnchor class ShapeContainer { public: + typedef RefVector< ShapeBase > ShapeVector; + explicit ShapeContainer( Drawing& rDrawing ); ~ShapeContainer(); @@ -106,9 +108,10 @@ public: const css::uno::Reference< css::drawing::XShapes >& rxShapes, const ShapeParentAnchor* pParentAnchor = nullptr ) const; + const ShapeVector & getAllShapes() const { return maShapes; } + private: typedef RefVector< ShapeType > ShapeTypeVector; - typedef RefVector< ShapeBase > ShapeVector; typedef RefMap< OUString, ShapeType > ShapeTypeMap; typedef RefMap< OUString, ShapeBase > ShapeMap; diff --git a/sc/source/filter/inc/commentsbuffer.hxx b/sc/source/filter/inc/commentsbuffer.hxx index 74e4d7cb123d..a1c6c8fb65b5 100644 --- a/sc/source/filter/inc/commentsbuffer.hxx +++ b/sc/source/filter/inc/commentsbuffer.hxx @@ -21,6 +21,7 @@ #include "richstring.hxx" #include "worksheethelper.hxx" +#include "drawingfragment.hxx" #include <com/sun/star/awt/Rectangle.hpp> namespace oox::xls { @@ -58,7 +59,7 @@ public: RichStringRef const & createText(); /** Finalizes the formatted string of the comment. */ - void finalizeImport(); + void finalizeImport(const VmlDrawing::NoteShapesMap& rNoteShapeMap); OUString getAuthorName(); diff --git a/sc/source/filter/inc/drawingfragment.hxx b/sc/source/filter/inc/drawingfragment.hxx index 820983a80262..64c7644e3ca3 100644 --- a/sc/source/filter/inc/drawingfragment.hxx +++ b/sc/source/filter/inc/drawingfragment.hxx @@ -141,11 +141,19 @@ private: class VmlDrawing final : public ::oox::vml::Drawing, public WorksheetHelper { public: + struct NoteShapesMapHash + { + size_t operator()(const std::pair<sal_Int32, sal_Int32> & key) const; + }; + using NoteShapesMap = std::unordered_map<std::pair<sal_Int32,sal_Int32>, const ::oox::vml::ShapeBase*, NoteShapesMapHash>; + explicit VmlDrawing( const WorksheetHelper& rHelper ); /** Returns the drawing shape for a cell note at the specified position. */ const ::oox::vml::ShapeBase* getNoteShape( const ScAddress& rPos ) const; + NoteShapesMap buildNoteShapesMap() const; + /** Filters cell note shapes. */ virtual bool isShapeSupported( const ::oox::vml::ShapeBase& rShape ) const override; diff --git a/sc/source/filter/oox/commentsbuffer.cxx b/sc/source/filter/oox/commentsbuffer.cxx index 1f1b0b242fde..cc01420cd0a2 100644 --- a/sc/source/filter/oox/commentsbuffer.cxx +++ b/sc/source/filter/oox/commentsbuffer.cxx @@ -188,7 +188,7 @@ namespace }; } -void Comment::finalizeImport() +void Comment::finalizeImport(const VmlDrawing::NoteShapesMap& rNoteShapesMap) { // BIFF12 stores cell range instead of cell address, use first cell of this range OSL_ENSURE( maModel.maRange.aStart == maModel.maRange.aEnd, @@ -221,8 +221,10 @@ void Comment::finalizeImport() // convert shape formatting and visibility bool bVisible = true; - if( const ::oox::vml::ShapeBase* pVmlNoteShape = getVmlDrawing().getNoteShape( maModel.maRange.aStart ) ) + auto it = rNoteShapesMap.find(std::make_pair(maModel.maRange.aStart.Col(), maModel.maRange.aStart.Row())); + if( it != rNoteShapesMap.end() ) { + const ::oox::vml::ShapeBase* pVmlNoteShape = it->second; // position and formatting css::awt::Rectangle aShapeRect = pVmlNoteShape->getShapeRectangle(); if (aShapeRect.Width > 0 || aShapeRect.Height > 0) @@ -313,7 +315,10 @@ void CommentsBuffer::finalizeImport() auto pModel = getScDocument().GetDrawLayer(); bool bWasLocked = pModel->isLocked(); pModel->setLock(true); - maComments.forEachMem( &Comment::finalizeImport ); + // build an index once, instead of scanning a potentially large list for every note. + VmlDrawing::NoteShapesMap aNoteShapesIndex = getVmlDrawing().buildNoteShapesMap(); + for (const std::shared_ptr<Comment> & rxComment : maComments) + rxComment->finalizeImport(aNoteShapesIndex); pModel->setLock(bWasLocked); } diff --git a/sc/source/filter/oox/drawingfragment.cxx b/sc/source/filter/oox/drawingfragment.cxx index 85e809b55591..6b58204ddc1d 100644 --- a/sc/source/filter/oox/drawingfragment.cxx +++ b/sc/source/filter/oox/drawingfragment.cxx @@ -483,6 +483,26 @@ const ::oox::vml::ShapeBase* VmlDrawing::getNoteShape( const ScAddress& rPos ) c return getShapes().findShape( VmlFindNoteFunc( rPos ) ); } +VmlDrawing::NoteShapesMap VmlDrawing::buildNoteShapesMap() const +{ + VmlDrawing::NoteShapesMap aMap; + for (const std::shared_ptr<oox::vml::ShapeBase> & rxShape : getShapes().getAllShapes()) + { + const ::oox::vml::ClientData* pClientData = rxShape->getClientData(); + if (pClientData) + aMap[std::make_pair(pClientData->mnCol, pClientData->mnRow)] = rxShape.get(); + } + return aMap; +} + +size_t VmlDrawing::NoteShapesMapHash::operator()(const std::pair<sal_Int32, sal_Int32>& rKey) const +{ + std::size_t seed = 0; + o3tl::hash_combine(seed, rKey.first); + o3tl::hash_combine(seed, rKey.second); + return seed; +} + bool VmlDrawing::isShapeSupported( const ::oox::vml::ShapeBase& rShape ) const { const ::oox::vml::ClientData* pClientData = rShape.getClientData();