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");

Reply via email to