include/tools/json_writer.hxx | 4 + include/vcl/weld.hxx | 6 ++ svx/source/inc/StylesPreviewWindow.hxx | 2 svx/source/tbxctrls/StylesPreviewWindow.cxx | 67 +++++++++++++++++++++++++++- vcl/inc/iconview.hxx | 8 +++ vcl/inc/salvtables.hxx | 3 + vcl/source/app/salvtables.cxx | 6 ++ vcl/source/treelist/iconview.cxx | 28 +++++++---- vcl/unx/gtk3/gtkinst.cxx | 5 ++ 9 files changed, 116 insertions(+), 13 deletions(-)
New commits: commit 1f0adaae53f3b04c44c0b0ef7f2497d7c62268c7 Author: Caolán McNamara <caolan.mcnam...@collabora.com> AuthorDate: Mon Jun 5 20:15:16 2023 +0100 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Tue Jun 6 09:49:06 2023 +0200 perf: cache the generated json for an iconview image before: |--13.95%--JSDialogNotifyIdle::Invoke | | | |--13.01%--JSDialogNotifyIdle::generateWidgetUpdate | | | | | |--11.03%--IconView::DumpAsPropertyTree | | | lcl_DumpEntryAndSiblings (inlined) | | | | | | | |--10.94%--extractPngString (inlined) after: |--4.86%--JSDialogNotifyIdle::Invoke ... | |--2.90%--JSDialogNotifyIdle::generateWidgetUpdate | | | | | |--0.76%--IconView::DumpAsPropertyTree | | | IconView::DumpEntryAndSiblings | | | Link<std::tuple<tools::JsonWriter&, rtl::OUString const&, std::basic_string_view<char, std::char_traits<char> > > const&, bool>::Call (inlined) | | | StylesPreviewWindow_Base::DoJsonProperty | | | | | | | |--0.55%--StylesPreviewWindow_Base::GetCachedPreviewJson Change-Id: Id234a84e36710794822945584be3adf028808625 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152650 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/include/tools/json_writer.hxx b/include/tools/json_writer.hxx index b8b47ddb2ef8..fd19e933e093 100644 --- a/include/tools/json_writer.hxx +++ b/include/tools/json_writer.hxx @@ -164,5 +164,7 @@ class ScopedJsonWriterStruct public: ~ScopedJsonWriterStruct() { mrWriter.endStruct(); } }; -}; + +typedef std::tuple<JsonWriter&, const OUString&, std::string_view> json_prop_query; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index b87af80565ab..07e12dff294d 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -70,6 +70,7 @@ typedef OutputDevice RenderContext; namespace tools { class JsonWriter; +typedef std::tuple<tools::JsonWriter&, const OUString&, std::string_view> json_prop_query; } class LOKTrigger; @@ -1436,6 +1437,11 @@ public: m_aQueryTooltipHdl = rLink; } + // 0: json writer, 1: id, 2: property. returns true if supported + virtual void + connect_get_property_tree_elem(const Link<const tools::json_prop_query&, bool>& rLink) + = 0; + virtual OUString get_selected_id() const = 0; virtual void clear() = 0; diff --git a/svx/source/inc/StylesPreviewWindow.hxx b/svx/source/inc/StylesPreviewWindow.hxx index 78ebcf94b13a..8eb16ba3901e 100644 --- a/svx/source/inc/StylesPreviewWindow.hxx +++ b/svx/source/inc/StylesPreviewWindow.hxx @@ -112,6 +112,7 @@ protected: DECL_LINK(Selected, weld::IconView&, void); DECL_LINK(DoubleClick, weld::IconView&, bool); DECL_LINK(DoCommand, const CommandEvent&, bool); + DECL_STATIC_LINK(StylesPreviewWindow_Base, DoJsonProperty, const tools::json_prop_query&, bool); public: StylesPreviewWindow_Base( @@ -122,6 +123,7 @@ public: void Select(const OUString& rStyleName); void RequestStylesListUpdate(); static VclPtr<VirtualDevice> GetCachedPreview(const std::pair<OUString, OUString>& rStyle); + static OString GetCachedPreviewJson(const OUString& rStyle); private: void UpdateStylesList(); diff --git a/svx/source/tbxctrls/StylesPreviewWindow.cxx b/svx/source/tbxctrls/StylesPreviewWindow.cxx index 81f8a5f8c71f..4f4be2ee5dff 100644 --- a/svx/source/tbxctrls/StylesPreviewWindow.cxx +++ b/svx/source/tbxctrls/StylesPreviewWindow.cxx @@ -19,6 +19,7 @@ #include <StylesPreviewWindow.hxx> +#include <comphelper/base64.hxx> #include <comphelper/propertyvalue.hxx> #include <utility> #include <vcl/svapp.hxx> @@ -28,6 +29,7 @@ #include <sfx2/sfxsids.hrc> #include <sfx2/tplpitem.hxx> #include <sfx2/viewsh.hxx> +#include <vcl/filter/PngImageWriter.hxx> #include <vcl/glyphitemcache.hxx> #include <vcl/virdev.hxx> #include <vcl/settings.hxx> @@ -59,16 +61,19 @@ #include <com/sun/star/uno/Sequence.hxx> #include <vcl/commandevent.hxx> +#include <tools/json_writer.hxx> namespace { class StylePreviewCache { static std::map<OUString, VclPtr<VirtualDevice>> gStylePreviewCache; + static std::map<OUString, OString> gJsonStylePreviewCache; static int gStylePreviewCacheClients; public: static std::map<OUString, VclPtr<VirtualDevice>>& Get() { return gStylePreviewCache; } + static std::map<OUString, OString>& GetJson() { return gJsonStylePreviewCache; } static void ClearCache() { @@ -76,6 +81,7 @@ public: aPreview.second.disposeAndClear(); gStylePreviewCache.clear(); + gJsonStylePreviewCache.clear(); } static void RegisterClient() { gStylePreviewCacheClients++; } @@ -88,6 +94,7 @@ public: }; std::map<OUString, VclPtr<VirtualDevice>> StylePreviewCache::gStylePreviewCache; +std::map<OUString, OString> StylePreviewCache::gJsonStylePreviewCache; int StylePreviewCache::gStylePreviewCacheClients; } @@ -414,6 +421,8 @@ StylesPreviewWindow_Base::StylesPreviewWindow_Base( m_xStylesView->connect_selection_changed(LINK(this, StylesPreviewWindow_Base, Selected)); m_xStylesView->connect_item_activated(LINK(this, StylesPreviewWindow_Base, DoubleClick)); m_xStylesView->connect_command(LINK(this, StylesPreviewWindow_Base, DoCommand)); + m_xStylesView->connect_get_property_tree_elem( + LINK(this, StylesPreviewWindow_Base, DoJsonProperty)); m_xStatusListener = new StyleStatusListener(this, xDispatchProvider); @@ -497,6 +506,44 @@ void StylesListUpdateTask::Invoke() m_rStylesList.UpdateSelection(); } +static OString extractPngString(const BitmapEx& rBitmap) +{ + SvMemoryStream aOStm(65535, 65535); + // Use fastest compression "1" + css::uno::Sequence<css::beans::PropertyValue> aFilterData{ + comphelper::makePropertyValue("Compression", sal_Int32(1)), + }; + vcl::PngImageWriter aPNGWriter(aOStm); + aPNGWriter.setParameters(aFilterData); + if (aPNGWriter.write(rBitmap)) + { + css::uno::Sequence<sal_Int8> aSeq(static_cast<sal_Int8 const*>(aOStm.GetData()), + aOStm.Tell()); + OStringBuffer aBuffer("data:image/png;base64,"); + ::comphelper::Base64::encode(aBuffer, aSeq); + return aBuffer.makeStringAndClear(); + } + + return ""; +} + +// 0: json writer, 1: id, 2: property. returns true if supported +IMPL_STATIC_LINK(StylesPreviewWindow_Base, DoJsonProperty, const tools::json_prop_query&, rQuery, + bool) +{ + if (std::get<2>(rQuery) != "image") + return false; + + OString sBase64Png(GetCachedPreviewJson(std::get<1>(rQuery))); + if (sBase64Png.isEmpty()) + return false; + + tools::JsonWriter& rJsonWriter = std::get<0>(rQuery); + rJsonWriter.put("image", sBase64Png); + + return true; +} + VclPtr<VirtualDevice> StylesPreviewWindow_Base::GetCachedPreview(const std::pair<OUString, OUString>& rStyle) { @@ -517,6 +564,23 @@ StylesPreviewWindow_Base::GetCachedPreview(const std::pair<OUString, OUString>& } } +OString StylesPreviewWindow_Base::GetCachedPreviewJson(const OUString& rStyle) +{ + auto aJsonFound = StylePreviewCache::GetJson().find(rStyle); + if (aJsonFound != StylePreviewCache::GetJson().end()) + return StylePreviewCache::GetJson()[rStyle]; + + auto aFound = StylePreviewCache::Get().find(rStyle); + if (aFound == StylePreviewCache::Get().end()) + return ""; + + VclPtr<VirtualDevice> xDev = aFound->second; + BitmapEx aBitmap(xDev->GetBitmapEx(Point(0, 0), xDev->GetOutputSize())); + OString sResult = extractPngString(aBitmap); + StylePreviewCache::GetJson()[rStyle] = sResult; + return sResult; +} + void StylesPreviewWindow_Base::UpdateStylesList() { m_aAllStyles = m_aDefaultStyles; @@ -536,7 +600,8 @@ void StylesPreviewWindow_Base::UpdateStylesList() while (pStyle) { - m_aAllStyles.push_back(std::pair<OUString, OUString>("", pStyle->GetName())); + OUString sName(pStyle->GetName()); + m_aAllStyles.push_back(std::pair<OUString, OUString>(sName, sName)); pStyle = xIter->Next(); } } diff --git a/vcl/inc/iconview.hxx b/vcl/inc/iconview.hxx index c5ece6d4aeb1..bc84a6ea436d 100644 --- a/vcl/inc/iconview.hxx +++ b/vcl/inc/iconview.hxx @@ -20,6 +20,7 @@ #ifndef INCLUDED_SVTOOLS_ICONVIEW_HXX #define INCLUDED_SVTOOLS_ICONVIEW_HXX +#include <tools/json_writer.hxx> #include <vcl/toolkit/treelistbox.hxx> class IconView final : public SvTreeListBox @@ -47,11 +48,18 @@ public: virtual FactoryFunction GetUITestFactory() const override; virtual void DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) override; + void SetDumpElemToPropertyTreeHdl(const Link<const tools::json_prop_query&, bool>& rLink) + { + maDumpElemToPropertyTreeHdl = rLink; + } + protected: virtual void CalcEntryHeight(SvTreeListEntry const* pEntry) override; private: Link<SvTreeListEntry*, OUString> maEntryAccessibleDescriptionHdl; + Link<const tools::json_prop_query&, bool> maDumpElemToPropertyTreeHdl; + void DumpEntryAndSiblings(tools::JsonWriter& rJsonWriter, SvTreeListEntry* pEntry); }; #endif diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx index 24622f069db5..7e33cbdbbd5b 100644 --- a/vcl/inc/salvtables.hxx +++ b/vcl/inc/salvtables.hxx @@ -1933,6 +1933,9 @@ public: virtual void connect_query_tooltip(const Link<const weld::TreeIter&, OUString>& rLink) override; + virtual void + connect_get_property_tree_elem(const Link<const tools::json_prop_query&, bool>& rLink) override; + virtual OUString get_selected_id() const override; virtual OUString get_selected_text() const override; diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index 38452ae84ac7..87a6722ca168 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -5518,6 +5518,12 @@ void SalInstanceIconView::connect_query_tooltip(const Link<const weld::TreeIter& m_xIconView->SetTooltipHdl(LINK(this, SalInstanceIconView, TooltipHdl)); } +void SalInstanceIconView::connect_get_property_tree_elem( + const Link<const tools::json_prop_query&, bool>& rLink) +{ + m_xIconView->SetDumpElemToPropertyTreeHdl(rLink); +} + OUString SalInstanceIconView::get_selected_id() const { assert(m_xIconView->IsUpdateMode() && "don't request selection when frozen"); diff --git a/vcl/source/treelist/iconview.cxx b/vcl/source/treelist/iconview.cxx index e658acdade6a..1b5b87c26c0f 100644 --- a/vcl/source/treelist/iconview.cxx +++ b/vcl/source/treelist/iconview.cxx @@ -282,8 +282,7 @@ static OString extractPngString(const SvLBoxContextBmp* pBmpItem) return ""; } -static void lcl_DumpEntryAndSiblings(tools::JsonWriter& rJsonWriter, SvTreeListEntry* pEntry, - const SvTreeListBox* pTabListBox) +void IconView::DumpEntryAndSiblings(tools::JsonWriter& rJsonWriter, SvTreeListEntry* pEntry) { while (pEntry) { @@ -294,24 +293,31 @@ static void lcl_DumpEntryAndSiblings(tools::JsonWriter& rJsonWriter, SvTreeListE if (pIt) rJsonWriter.put("text", static_cast<const SvLBoxString*>(pIt)->GetText()); - pIt = pEntry->GetFirstItem(SvLBoxItemType::ContextBmp); - if (pIt) + const OUString* pId = static_cast<const OUString*>(pEntry->GetUserData()); + const bool bHandled = pId && maDumpElemToPropertyTreeHdl.IsSet() + && maDumpElemToPropertyTreeHdl.Call( + tools::json_prop_query(rJsonWriter, *pId, "image")); + if (!bHandled) { - const SvLBoxContextBmp* pBmpItem = static_cast<const SvLBoxContextBmp*>(pIt); - if (pBmpItem) - rJsonWriter.put("image", extractPngString(pBmpItem)); + pIt = pEntry->GetFirstItem(SvLBoxItemType::ContextBmp); + if (pIt) + { + const SvLBoxContextBmp* pBmpItem = static_cast<const SvLBoxContextBmp*>(pIt); + if (pBmpItem) + rJsonWriter.put("image", extractPngString(pBmpItem)); + } } - if (const OUString tooltip = pTabListBox->GetEntryTooltip(pEntry); !tooltip.isEmpty()) + if (const OUString tooltip = GetEntryTooltip(pEntry); !tooltip.isEmpty()) rJsonWriter.put("tooltip", tooltip); - if (pTabListBox->IsSelected(pEntry)) + if (IsSelected(pEntry)) rJsonWriter.put("selected", true); if (pEntry->GetFlags() & SvTLEntryFlags::IS_SEPARATOR) rJsonWriter.put("separator", true); - rJsonWriter.put("row", pTabListBox->GetModel()->GetAbsPos(pEntry)); + rJsonWriter.put("row", GetModel()->GetAbsPos(pEntry)); pEntry = pEntry->NextSibling(); } @@ -323,7 +329,7 @@ void IconView::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) rJsonWriter.put("type", "iconview"); rJsonWriter.put("singleclickactivate", GetActivateOnSingleClick()); auto aNode = rJsonWriter.startArray("entries"); - lcl_DumpEntryAndSiblings(rJsonWriter, First(), this); + DumpEntryAndSiblings(rJsonWriter, First()); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx index 2d4a2c7cf70f..9e38d781d204 100644 --- a/vcl/unx/gtk3/gtkinst.cxx +++ b/vcl/unx/gtk3/gtkinst.cxx @@ -16978,6 +16978,11 @@ public: gtk_widget_set_has_tooltip(GTK_WIDGET(m_pIconView), true); } + virtual void connect_get_property_tree_elem(const Link<const tools::json_prop_query&, bool>& /*rLink*/) override + { + //not implemented for the gtk variant + } + virtual OUString get_selected_id() const override { assert(gtk_icon_view_get_model(m_pIconView) && "don't request selection when frozen");