include/svx/fontworkgallery.hxx              |    2 -
 include/vcl/weld.hxx                         |    7 +++---
 sd/source/ui/sidebar/MasterPagesSelector.cxx |   16 ++++++++++++-
 sd/source/ui/sidebar/MasterPagesSelector.hxx |    1 
 starmath/source/ElementsDockingWindow.cxx    |    3 +-
 svx/source/inc/StylesPreviewWindow.hxx       |    2 -
 svx/source/tbxctrls/StylesPreviewWindow.cxx  |   31 +++++++++++++--------------
 svx/source/tbxctrls/fontworkgallery.cxx      |    6 ++---
 vcl/inc/jsdialog/jsdialogbuilder.hxx         |    4 +--
 vcl/inc/qt5/QtInstanceIconView.hxx           |    4 +--
 vcl/inc/salvtables.hxx                       |    4 +--
 vcl/jsdialog/jsdialogbuilder.cxx             |    4 +--
 vcl/qt5/QtInstanceIconView.cxx               |    4 +--
 vcl/source/app/salvtables.cxx                |    6 +----
 vcl/unx/gtk3/gtkinst.cxx                     |   12 ++++++++--
 15 files changed, 64 insertions(+), 42 deletions(-)

New commits:
commit 25f007984f91393dbc34adbd2bf59d8b4bae490a
Author:     Noel Grandin <noelgran...@gmail.com>
AuthorDate: Tue Jun 10 18:20:18 2025 +0200
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Wed Jun 11 17:11:29 2025 +0200

    tdf#166932 use BitmapEx in preview style cache instead of VirtualDevice
    
    which reduces the number of open GDI handles we need
    
    Change-Id: I9620d120b9ec88d972908eb713560e414384dec7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186333
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    Tested-by: Jenkins
    (cherry picked from commit 8899ae01feb9b9088372d76a6d0562397174c89c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186352
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/include/svx/fontworkgallery.hxx b/include/svx/fontworkgallery.hxx
index 21c418584570..a91aa0a95f90 100644
--- a/include/svx/fontworkgallery.hxx
+++ b/include/svx/fontworkgallery.hxx
@@ -53,7 +53,7 @@ class SAL_WARN_UNUSED SVXCORE_DLLPUBLIC FontWorkGalleryDialog 
final : public wel
     rtl::Reference<SdrObject> mxSdrObject;
     SdrModel*           mpDestModel;
 
-    std::vector<VclPtr< VirtualDevice >> maFavoritesHorizontal;
+    std::vector<BitmapEx> maFavoritesHorizontal;
     // mapping between item ID and item title
     std::map<OUString, OUString> maIdToTitleMap;
 
diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index b78bb73092ee..ce231ca7977a 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -60,6 +60,7 @@ class TransferDataContainer;
 class OutputDevice;
 class VirtualDevice;
 struct SystemEnvData;
+class BitmapEx;
 
 namespace vcl
 {
@@ -1461,8 +1462,8 @@ public:
                         const OUString* pIconName, TreeIter* pRet)
         = 0;
 
-    virtual void insert(int pos, const OUString* pStr, const OUString* pId,
-                        const VirtualDevice* pIcon, TreeIter* pRet)
+    virtual void insert(int pos, const OUString* pStr, const OUString* pId, 
const BitmapEx* pIcon,
+                        TreeIter* pRet)
         = 0;
 
     virtual void insert_separator(int pos, const OUString* pId) = 0;
@@ -1472,7 +1473,7 @@ public:
         insert(-1, &rStr, &rId, &rImage, nullptr);
     }
 
-    void append(const OUString& rId, const OUString& rStr, const 
VirtualDevice* pImage)
+    void append(const OUString& rId, const OUString& rStr, const BitmapEx* 
pImage)
     {
         insert(-1, &rStr, &rId, pImage, nullptr);
     }
diff --git a/sd/source/ui/sidebar/MasterPagesSelector.cxx 
b/sd/source/ui/sidebar/MasterPagesSelector.cxx
index b40704c307de..13ba8a88180f 100644
--- a/sd/source/ui/sidebar/MasterPagesSelector.cxx
+++ b/sd/source/ui/sidebar/MasterPagesSelector.cxx
@@ -391,16 +391,18 @@ void MasterPagesSelector::SetItem (
 
         if (aPreview.GetSizePixel().Width() > 0)
         {
-            VclPtr<VirtualDevice> pVDev = GetVirtualDevice(aPreview);
             if (!mxPreviewIconView->get_id(nIndex).isEmpty())
             {
+                VclPtr<VirtualDevice> pVDev = GetVirtualDevice(aPreview);
                 mxPreviewIconView->set_image(nIndex, *pVDev);
                 mxPreviewIconView->set_id(nIndex, OUString::number(aToken));
+                pVDev.disposeAndClear();
             }
             else
             {
+                BitmapEx aPreviewBitmap = GetPreviewAsBitmap(aPreview);
                 OUString sId = OUString::number(aToken);
-                mxPreviewIconView->insert(nIndex, nullptr, &sId, pVDev, 
nullptr);
+                mxPreviewIconView->insert(nIndex, nullptr, &sId, 
&aPreviewBitmap, nullptr);
                 mxPreviewIconView->set_item_accessible_name(
                     nIndex, mpContainer->GetPageNameForToken(aToken));
             }
@@ -481,6 +483,16 @@ VclPtr<VirtualDevice> 
MasterPagesSelector::GetVirtualDevice(const Image& rImage)
     return pVDev;
 }
 
+BitmapEx MasterPagesSelector::GetPreviewAsBitmap(const Image& rImage)
+{
+    BitmapEx aPreviewBitmap = rImage.GetBitmapEx();
+    ScopedVclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create();
+    if (pVDev->GetDPIScaleFactor() > 1)
+        aPreviewBitmap.Scale(pVDev->GetDPIScaleFactor(), 
pVDev->GetDPIScaleFactor());
+
+    return aPreviewBitmap;
+}
+
 void MasterPagesSelector::UpdateAllPreviews()
 {
     const ::osl::MutexGuard aGuard (maMutex);
diff --git a/sd/source/ui/sidebar/MasterPagesSelector.hxx 
b/sd/source/ui/sidebar/MasterPagesSelector.hxx
index 3731a491da74..0c8a36fd5e43 100644
--- a/sd/source/ui/sidebar/MasterPagesSelector.hxx
+++ b/sd/source/ui/sidebar/MasterPagesSelector.hxx
@@ -171,6 +171,7 @@ private:
         MasterPageContainer::Token aToken);
 
     static VclPtr<VirtualDevice> GetVirtualDevice(const Image& rPreview);
+    static BitmapEx GetPreviewAsBitmap(const Image& rPreview);
 };
 
 } // end of namespace sd::sidebar
diff --git a/starmath/source/ElementsDockingWindow.cxx 
b/starmath/source/ElementsDockingWindow.cxx
index f96d4647c0b9..cfba577cb155 100644
--- a/starmath/source/ElementsDockingWindow.cxx
+++ b/starmath/source/ElementsDockingWindow.cxx
@@ -594,7 +594,8 @@ void SmElementsControl::addElement(const OUString& 
aElementVisual, const OUStrin
 
     maItemDatas.push_back(std::make_unique<ElementData>(aElementSource, 
aHelpText, maItemDatas.size()));
     const OUString aId(weld::toId(maItemDatas.back().get()));
-    mpIconView->insert(-1, nullptr, &aId, pDevice, nullptr);
+    BitmapEx aBitmap = pDevice->GetBitmapEx(Point(0,0), 
pDevice->GetOutputSizePixel());
+    mpIconView->insert(-1, nullptr, &aId, &aBitmap, nullptr);
     mpIconView->set_item_accessible_name(mpIconView->n_children() - 1, 
GetElementHelpText(aId));
     if (mpIconView->get_item_width() < aSize.Width())
         mpIconView->set_item_width(aSize.Width());
diff --git a/svx/source/inc/StylesPreviewWindow.hxx 
b/svx/source/inc/StylesPreviewWindow.hxx
index 455462659079..0cbee0c31747 100644
--- a/svx/source/inc/StylesPreviewWindow.hxx
+++ b/svx/source/inc/StylesPreviewWindow.hxx
@@ -123,7 +123,7 @@ public:
 
     void Select(const OUString& rStyleName);
     void RequestStylesListUpdate();
-    static VclPtr<VirtualDevice> GetCachedPreview(const std::pair<OUString, 
OUString>& rStyle);
+    static BitmapEx GetCachedPreview(const std::pair<OUString, OUString>& 
rStyle);
     static OString GetCachedPreviewJson(const std::pair<OUString, OUString>& 
rStyle);
 
 private:
diff --git a/svx/source/tbxctrls/StylesPreviewWindow.cxx 
b/svx/source/tbxctrls/StylesPreviewWindow.cxx
index 7b2c63931e96..f3684c13c743 100644
--- a/svx/source/tbxctrls/StylesPreviewWindow.cxx
+++ b/svx/source/tbxctrls/StylesPreviewWindow.cxx
@@ -80,20 +80,17 @@ private:
         virtual void Invoke() override { 
StylePreviewCache::gJsonStylePreviewCache.clear(); }
     };
 
-    static std::map<OUString, VclPtr<VirtualDevice>> gStylePreviewCache;
+    static std::map<OUString, BitmapEx> gStylePreviewCache;
     static std::map<OUString, OString> gJsonStylePreviewCache;
     static int gStylePreviewCacheClients;
     static JsonStylePreviewCacheClear gJsonIdleClear;
 
 public:
-    static std::map<OUString, VclPtr<VirtualDevice>>& Get() { return 
gStylePreviewCache; }
+    static std::map<OUString, BitmapEx>& Get() { return gStylePreviewCache; }
     static std::map<OUString, OString>& GetJson() { return 
gJsonStylePreviewCache; }
 
     static void ClearCache(bool bHard)
     {
-        for (auto& aPreview : gStylePreviewCache)
-            aPreview.second.disposeAndClear();
-
         gStylePreviewCache.clear();
         if (bHard)
         {
@@ -122,7 +119,7 @@ public:
     }
 };
 
-std::map<OUString, VclPtr<VirtualDevice>> 
StylePreviewCache::gStylePreviewCache;
+std::map<OUString, BitmapEx> StylePreviewCache::gStylePreviewCache;
 std::map<OUString, OString> StylePreviewCache::gJsonStylePreviewCache;
 int StylePreviewCache::gStylePreviewCacheClients;
 StylePreviewCache::JsonStylePreviewCacheClear 
StylePreviewCache::gJsonIdleClear;
@@ -580,23 +577,23 @@ IMPL_LINK(StylesPreviewWindow_Base, GetPreviewImage, 
const weld::encoded_image_q
     return true;
 }
 
-VclPtr<VirtualDevice>
-StylesPreviewWindow_Base::GetCachedPreview(const std::pair<OUString, 
OUString>& rStyle)
+BitmapEx StylesPreviewWindow_Base::GetCachedPreview(const std::pair<OUString, 
OUString>& rStyle)
 {
     auto aFound = StylePreviewCache::Get().find(rStyle.second);
     if (aFound != StylePreviewCache::Get().end())
         return StylePreviewCache::Get()[rStyle.second];
     else
     {
-        VclPtr<VirtualDevice> pImg = VclPtr<VirtualDevice>::Create();
+        ScopedVclPtrInstance<VirtualDevice> pImg;
         const Size aSize(100, 30);
         pImg->SetOutputSizePixel(aSize);
 
         StyleItemController aStyleController(rStyle);
         aStyleController.Paint(*pImg);
-        StylePreviewCache::Get()[rStyle.second] = pImg;
+        BitmapEx aBitmap = pImg->GetBitmapEx(Point(0, 0), aSize);
+        StylePreviewCache::Get()[rStyle.second] = aBitmap;
 
-        return pImg;
+        return aBitmap;
     }
 }
 
@@ -606,8 +603,7 @@ OString 
StylesPreviewWindow_Base::GetCachedPreviewJson(const std::pair<OUString,
     if (aJsonFound != StylePreviewCache::GetJson().end())
         return StylePreviewCache::GetJson()[rStyle.second];
 
-    VclPtr<VirtualDevice> xDev = GetCachedPreview(rStyle);
-    BitmapEx aBitmap(xDev->GetBitmapEx(Point(0, 0), xDev->GetOutputSize()));
+    BitmapEx aBitmap = GetCachedPreview(rStyle);
     OString sResult = extractPngString(aBitmap);
     StylePreviewCache::GetJson()[rStyle.second] = sResult;
     return sResult;
@@ -645,8 +641,13 @@ void StylesPreviewWindow_Base::UpdateStylesList()
     const bool bNeedInsertPreview = !comphelper::LibreOfficeKit::isActive();
     for (const auto& rStyle : m_aAllStyles)
     {
-        VclPtr<VirtualDevice> pImg = bNeedInsertPreview ? 
GetCachedPreview(rStyle) : nullptr;
-        m_xStylesView->append(rStyle.first, rStyle.second, pImg);
+        if (bNeedInsertPreview)
+        {
+            BitmapEx aPreview = GetCachedPreview(rStyle);
+            m_xStylesView->append(rStyle.first, rStyle.second, &aPreview);
+        }
+        else
+            m_xStylesView->append(rStyle.first, rStyle.second, nullptr);
     }
     m_xStylesView->thaw();
 }
diff --git a/svx/source/tbxctrls/fontworkgallery.cxx 
b/svx/source/tbxctrls/fontworkgallery.cxx
index 7c8f2b789525..e1b1e1baad7f 100644
--- a/svx/source/tbxctrls/fontworkgallery.cxx
+++ b/svx/source/tbxctrls/fontworkgallery.cxx
@@ -100,7 +100,7 @@ void FontWorkGalleryDialog::initFavorites(sal_uInt16 
nThemeId)
 
         if (GalleryExplorer::GetSdrObj(nThemeId, nModelPos, pModel, &aThumb) 
&& !aThumb.IsEmpty())
         {
-            VclPtr< VirtualDevice > pVDev = VclPtr<VirtualDevice>::Create();
+            ScopedVclPtrInstance< VirtualDevice > pVDev;
             const Point aNull(0, 0);
 
             if (pVDev->GetDPIScaleFactor() > 1)
@@ -117,7 +117,7 @@ void FontWorkGalleryDialog::initFavorites(sal_uInt16 
nThemeId)
             pVDev->DrawCheckered(aNull, aSize, nLen, aW, aG);
 
             pVDev->DrawBitmapEx(aNull, aThumb);
-            maFavoritesHorizontal.emplace_back(pVDev);
+            maFavoritesHorizontal.emplace_back(pVDev->GetBitmapEx(Point(0,0), 
aSize));
         }
     }
 
@@ -142,7 +142,7 @@ void FontWorkGalleryDialog::fillFavorites(sal_uInt16 
nThemeId)
     {
         OUString sId = OUString::number(static_cast<sal_uInt16>(nFavorite));
         maIdToTitleMap.emplace(sId, aTitles.at(nFavorite - 1));
-        maCtlFavorites->insert(-1, nullptr, &sId, 
maFavoritesHorizontal[nFavorite - 1], nullptr);
+        maCtlFavorites->insert(-1, nullptr, &sId, 
&maFavoritesHorizontal[nFavorite - 1], nullptr);
         maCtlFavorites->set_item_accessible_name(maCtlFavorites->n_children() 
- 1, aTitles.at(nFavorite -1));
     }
 
diff --git a/vcl/inc/jsdialog/jsdialogbuilder.hxx 
b/vcl/inc/jsdialog/jsdialogbuilder.hxx
index 23c2cc1b8abd..18df577e78b5 100644
--- a/vcl/inc/jsdialog/jsdialogbuilder.hxx
+++ b/vcl/inc/jsdialog/jsdialogbuilder.hxx
@@ -741,8 +741,8 @@ public:
     virtual void insert(int pos, const OUString* pStr, const OUString* pId,
                         const OUString* pIconName, weld::TreeIter* pRet) 
override;
 
-    virtual void insert(int pos, const OUString* pStr, const OUString* pId,
-                        const VirtualDevice* pIcon, weld::TreeIter* pRet) 
override;
+    virtual void insert(int pos, const OUString* pStr, const OUString* pId, 
const BitmapEx* pIcon,
+                        weld::TreeIter* pRet) override;
 
     virtual void insert_separator(int pos, const OUString* pId) override;
 
diff --git a/vcl/inc/qt5/QtInstanceIconView.hxx 
b/vcl/inc/qt5/QtInstanceIconView.hxx
index 274d92ab8886..8cff84249a33 100644
--- a/vcl/inc/qt5/QtInstanceIconView.hxx
+++ b/vcl/inc/qt5/QtInstanceIconView.hxx
@@ -31,8 +31,8 @@ public:
     virtual void insert(int nPos, const OUString* pStr, const OUString* pId,
                         const OUString* pIconName, weld::TreeIter* pRet) 
override;
 
-    virtual void insert(int nPos, const OUString* pStr, const OUString* pId,
-                        const VirtualDevice* pIcon, weld::TreeIter* pRet) 
override;
+    virtual void insert(int nPos, const OUString* pStr, const OUString* pId, 
const BitmapEx* pIcon,
+                        weld::TreeIter* pRet) override;
 
     virtual void insert_separator(int pos, const OUString* pId) override;
 
diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx
index a057e67f96fa..3f9d3238c7c1 100644
--- a/vcl/inc/salvtables.hxx
+++ b/vcl/inc/salvtables.hxx
@@ -1942,8 +1942,8 @@ public:
     virtual void insert(int pos, const OUString* pStr, const OUString* pId,
                         const OUString* pIconName, weld::TreeIter* pRet) 
override;
 
-    virtual void insert(int pos, const OUString* pStr, const OUString* pId,
-                        const VirtualDevice* pIcon, weld::TreeIter* pRet) 
override;
+    virtual void insert(int pos, const OUString* pStr, const OUString* pId, 
const BitmapEx* pIcon,
+                        weld::TreeIter* pRet) override;
 
     virtual void insert_separator(int pos, const OUString* pId) override;
 
diff --git a/vcl/jsdialog/jsdialogbuilder.cxx b/vcl/jsdialog/jsdialogbuilder.cxx
index 219e72c2b98b..b54ecf9855be 100644
--- a/vcl/jsdialog/jsdialogbuilder.cxx
+++ b/vcl/jsdialog/jsdialogbuilder.cxx
@@ -1887,8 +1887,8 @@ void JSIconView::insert(int pos, const OUString* pStr, 
const OUString* pId,
     sendUpdate();
 }
 
-void JSIconView::insert(int pos, const OUString* pStr, const OUString* pId,
-                        const VirtualDevice* pIcon, weld::TreeIter* pRet)
+void JSIconView::insert(int pos, const OUString* pStr, const OUString* pId, 
const BitmapEx* pIcon,
+                        weld::TreeIter* pRet)
 {
     SalInstanceIconView::insert(pos, pStr, pId, pIcon, pRet);
     sendUpdate();
diff --git a/vcl/qt5/QtInstanceIconView.cxx b/vcl/qt5/QtInstanceIconView.cxx
index d943b508652d..041041584f43 100644
--- a/vcl/qt5/QtInstanceIconView.cxx
+++ b/vcl/qt5/QtInstanceIconView.cxx
@@ -55,7 +55,7 @@ void QtInstanceIconView::insert(int, const OUString*, const 
OUString*, const OUS
 }
 
 void QtInstanceIconView::insert(int nPos, const OUString* pStr, const 
OUString* pId,
-                                const VirtualDevice* pIcon, weld::TreeIter* 
pRet)
+                                const BitmapEx* pIcon, weld::TreeIter* pRet)
 {
     assert(!pRet && "Support for pRet param not implemented yet");
     (void)pRet;
@@ -76,7 +76,7 @@ void QtInstanceIconView::insert(int nPos, const OUString* 
pStr, const OUString*
             pItem->setIcon(QIcon(toQPixmap(*pIcon)));
             // set list view icon size to avoid downscaling
             const QSize aIconSize
-                = 
m_pListView->iconSize().expandedTo(toQSize(pIcon->GetOutputSizePixel()));
+                = 
m_pListView->iconSize().expandedTo(toQSize(pIcon->GetSizePixel()));
             m_pListView->setIconSize(aIconSize);
         }
 
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 8c94778ae86f..9860a5224a8e 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -5494,7 +5494,7 @@ void SalInstanceIconView::insert(int pos, const OUString* 
pStr, const OUString*
 }
 
 void SalInstanceIconView::insert(int pos, const OUString* pStr, const 
OUString* pId,
-                                 const VirtualDevice* pIcon, weld::TreeIter* 
pRet)
+                                 const BitmapEx* pIcon, weld::TreeIter* pRet)
 {
     disable_notify_events();
     auto nInsertPos = pos == -1 ? TREELIST_APPEND : pos;
@@ -5510,9 +5510,7 @@ void SalInstanceIconView::insert(int pos, const OUString* 
pStr, const OUString*
     SvTreeListEntry* pEntry = new SvTreeListEntry;
     if (pIcon)
     {
-        const Point aNull(0, 0);
-        const Size aSize = pIcon->GetOutputSize();
-        Image aImage(pIcon->GetBitmapEx(aNull, aSize));
+        Image aImage(*pIcon);
         pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aImage, aImage, 
false));
     }
     else
diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index df4594db17ff..f000864d082e 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -4937,6 +4937,14 @@ namespace
         return pRet;
     }
 
+    GdkPixbuf* getPixbuf(const BitmapEx& rBitmap)
+    {
+        ScopedVclPtr<VirtualDevice> pVDevice(VclPtr<VirtualDevice>::Create());
+        pVDevice->SetOutputSizePixel(rBitmap.GetSizePixel());
+        pVDevice->DrawBitmapEx(Point(0,0), rBitmap);
+        return getPixbuf(*pVDevice);
+    }
+
 #if GTK_CHECK_VERSION(4, 0, 0)
     cairo_surface_t* render_paintable_to_surface(GdkPaintable *paintable, int 
nWidth, int nHeight)
     {
@@ -17122,7 +17130,7 @@ private:
         }
     }
 
-    void insert_item(GtkTreeIter& iter, int pos, const OUString* pId, const 
OUString* pText, const VirtualDevice* pIcon)
+    void insert_item(GtkTreeIter& iter, int pos, const OUString* pId, const 
OUString* pText, const BitmapEx* pIcon)
     {
         // m_nTextCol may be -1, so pass it last, to not terminate the 
sequence before the Id value
         gtk_tree_store_insert_with_values(m_pTreeStore, &iter, nullptr, pos,
@@ -17324,7 +17332,7 @@ public:
         enable_notify_events();
     }
 
-    virtual void insert(int pos, const OUString* pText, const OUString* pId, 
const VirtualDevice* pIcon, weld::TreeIter* pRet) override
+    virtual void insert(int pos, const OUString* pText, const OUString* pId, 
const BitmapEx* pIcon, weld::TreeIter* pRet) override
     {
         disable_notify_events();
         GtkTreeIter iter;

Reply via email to