include/svx/SvxColorIconView.hxx         |    2 +
 svx/source/tbxctrls/Palette.cxx          |   10 ++++--
 svx/source/tbxctrls/PaletteManager.cxx   |   16 ++++++----
 svx/source/tbxctrls/SvxColorIconView.cxx |   46 +++++++++++++++++--------------
 4 files changed, 43 insertions(+), 31 deletions(-)

New commits:
commit 034380cbb364c44fbd8aa6dad858828f89ba80d8
Author:     Andras Timar <[email protected]>
AuthorDate: Fri Feb 20 17:12:34 2026 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Fri Feb 27 11:50:46 2026 +0100

    color icon view: reuse single VirtualDevice per palette load
    
    Follow-up to commit c5f6227d8e71 (use ScopedVclPtr to avoid leaks).
    That fix ensures each per-color VirtualDevice is properly disposed, but
    still creates and destroys ~120 VDs per palette load — each one
    allocating a GDI DC + DIB section on Windows.
    
    Reuse a single VirtualDevice across the entire loop instead: create it
    once via createColorDevice(), redraw it for each color via drawColor(),
    and pass it to IconView::insert() which copies the bitmap immediately.
    This reduces GDI allocations from ~120 to 1 per palette population.
    
    Also removes the GetBitmapEx+Scale+DrawBitmapEx roundtrip from
    createColorVirtualDevice: the VD is now created at the correct
    DPI-scaled size from the start, eliminating an extra temporary GDI
    bitmap allocation per entry at HiDPI.
    
    Change-Id: I676cdf49cbd34a4a01b46f8ba516bf34a19ff23c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199894
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Mike Kaganski <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200590
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/include/svx/SvxColorIconView.hxx b/include/svx/SvxColorIconView.hxx
index bb7c8d9eb7c2..1ef4f20aa3a4 100644
--- a/include/svx/SvxColorIconView.hxx
+++ b/include/svx/SvxColorIconView.hxx
@@ -34,6 +34,8 @@ public:
     static void addEntriesForColorSet(weld::IconView& pIconView, const 
std::set<Color>& rColorSet,
                                       std::u16string_view rNamePrefix);
 
+    static VclPtr<VirtualDevice> createColorDevice();
+    static void drawColor(VirtualDevice& rDev, const Color& rColor);
     static ScopedVclPtr<VirtualDevice> createColorVirtualDevice(const Color& 
rColor);
 };
 
diff --git a/svx/source/tbxctrls/Palette.cxx b/svx/source/tbxctrls/Palette.cxx
index c06de53f9458..c01636c71dc8 100644
--- a/svx/source/tbxctrls/Palette.cxx
+++ b/svx/source/tbxctrls/Palette.cxx
@@ -57,10 +57,11 @@ void PaletteASE::LoadColorSet(weld::IconView& pIconView)
 {
     pIconView.clear();
     int nIx = 0;
+    ScopedVclPtr<VirtualDevice> pVDev = SvxColorIconView::createColorDevice();
     for (const auto& rColor : maColors)
     {
-        auto pColorVDev = 
SvxColorIconView::createColorVirtualDevice(rColor.m_aColor);
-        Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+        SvxColorIconView::drawColor(*pVDev, rColor.m_aColor);
+        Bitmap aBmp = pVDev->GetBitmap(Point(), pVDev->GetOutputSizePixel());
         OUString sColorName = rColor.m_aName;
         OUString sId = OUString::number(nIx);
         pIconView.insert(nIx, &sColorName, &sId, &aBmp, nullptr);
@@ -335,10 +336,11 @@ void PaletteGPL::LoadColorSet(weld::IconView& pIconView)
 
     pIconView.clear();
     int nIx = 0;
+    ScopedVclPtr<VirtualDevice> pVDev = SvxColorIconView::createColorDevice();
     for (const auto& rColor : maColors)
     {
-        auto pColorVDev = 
SvxColorIconView::createColorVirtualDevice(rColor.m_aColor);
-        Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+        SvxColorIconView::drawColor(*pVDev, rColor.m_aColor);
+        Bitmap aBmp = pVDev->GetBitmap(Point(), pVDev->GetOutputSizePixel());
         OUString sColorName = rColor.m_aName;
         OUString sId = OUString::number(nIx);
         pIconView.insert(nIx, &sColorName, &sId, &aBmp, nullptr);
diff --git a/svx/source/tbxctrls/PaletteManager.cxx 
b/svx/source/tbxctrls/PaletteManager.cxx
index 7163c2cbcdc3..2e0e14849104 100644
--- a/svx/source/tbxctrls/PaletteManager.cxx
+++ b/svx/source/tbxctrls/PaletteManager.cxx
@@ -250,11 +250,12 @@ void PaletteManager::ReloadColorSet(weld::IconView 
&pIconView)
         pIconView.clear();
         css::uno::Sequence< sal_Int32 > CustomColorList( 
officecfg::Office::Common::UserColors::CustomColor::get() );
         css::uno::Sequence< OUString > CustomColorNameList( 
officecfg::Office::Common::UserColors::CustomColorName::get() );
+        ScopedVclPtr<VirtualDevice> pVDev = 
SvxColorIconView::createColorDevice();
         for (int i = 0; i < CustomColorList.getLength(); ++i)
         {
             Color aColor(ColorTransparency, CustomColorList[i]);
-            auto pColorVDev = 
SvxColorIconView::createColorVirtualDevice(aColor);
-            Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+            SvxColorIconView::drawColor(*pVDev, aColor);
+            Bitmap aBmp = pVDev->GetBitmap(Point(), 
pVDev->GetOutputSizePixel());
             OUString sId = OUString::number(i);
             OUString sColorName = CustomColorNameList[i];
             pIconView.insert(i, &sColorName, &sId, &aBmp, nullptr);
@@ -277,15 +278,15 @@ void PaletteManager::ReloadColorSet(weld::IconView 
&pIconView)
             moThemePaletteCollection = aThemeColorManager.generate();
 
             // Each row is one effect type (no effect + each type).
+            ScopedVclPtr<VirtualDevice> pVDev = 
SvxColorIconView::createColorDevice();
             for (size_t nEffect : {0, 1, 2, 3, 4, 5})
             {
                 // Each column is one color type.
                 for (auto const& rColorData : 
moThemePaletteCollection->maColors)
                 {
                     auto const& rEffect = rColorData.maEffects[nEffect];
-                    Color aColor = rEffect.maColor;
-                    auto pColorVDev = 
SvxColorIconView::createColorVirtualDevice(aColor);
-                    Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+                    SvxColorIconView::drawColor(*pVDev, rEffect.maColor);
+                    Bitmap aBmp = pVDev->GetBitmap(Point(), 
pVDev->GetOutputSizePixel());
                     OUString sColorName = rEffect.maColorName;
                     OUString sId = OUString::number(nItemId);
                     pIconView.insert(nItemId, &sColorName, &sId, &aBmp, 
nullptr);
@@ -321,11 +322,12 @@ void PaletteManager::ReloadRecentColorSet(weld::IconView& 
pIconView)
     css::uno::Sequence< OUString > 
ColorNamelist(officecfg::Office::Common::UserColors::RecentColorName::get());
     int nIx = 0;
     const bool bHasColorNames = Colorlist.getLength() == 
ColorNamelist.getLength();
+    ScopedVclPtr<VirtualDevice> pVDev = SvxColorIconView::createColorDevice();
     for (int i = 0; i < Colorlist.getLength(); ++i)
     {
         Color aColor(ColorTransparency, Colorlist[i]);
-        auto pColorVDev = SvxColorIconView::createColorVirtualDevice(aColor);
-        Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+        SvxColorIconView::drawColor(*pVDev, aColor);
+        Bitmap aBmp = pVDev->GetBitmap(Point(), pVDev->GetOutputSizePixel());
         OUString sColorName = bHasColorNames ? ColorNamelist[i] : ("#" + 
aColor.AsRGBHexString().toAsciiUpperCase());
         maRecentColors.emplace_back(aColor, sColorName);
         OUString sId = OUString::number(nIx);
diff --git a/svx/source/tbxctrls/SvxColorIconView.cxx 
b/svx/source/tbxctrls/SvxColorIconView.cxx
index 66b23eb8cdbd..96e102a14fd9 100644
--- a/svx/source/tbxctrls/SvxColorIconView.cxx
+++ b/svx/source/tbxctrls/SvxColorIconView.cxx
@@ -42,6 +42,7 @@ void 
SvxColorIconView::addEntriesForXColorList(weld::IconView& pIconView,
                                                sal_uInt32 nStartIndex)
 {
     const sal_uInt32 nColorCount(rXColorList.Count());
+    ScopedVclPtr<VirtualDevice> pVDev = createColorDevice();
 
     for (sal_uInt32 nIndex(0); nIndex < nColorCount; nIndex++, nStartIndex++)
     {
@@ -49,8 +50,8 @@ void 
SvxColorIconView::addEntriesForXColorList(weld::IconView& pIconView,
 
         if (pEntry)
         {
-            auto pColorVDev = createColorVirtualDevice(pEntry->GetColor());
-            Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+            drawColor(*pVDev, pEntry->GetColor());
+            Bitmap aBmp = pVDev->GetBitmap(Point(), 
pVDev->GetOutputSizePixel());
             OUString sColorName = pEntry->GetName();
             OUString sId = OUString::number(nIndex);
             pIconView.insert(nIndex, &sColorName, &sId, &aBmp, nullptr);
@@ -67,12 +68,14 @@ void 
SvxColorIconView::addEntriesForColorSet(weld::IconView& pIconView,
                                              std::u16string_view rNamePrefix)
 {
     sal_uInt32 nStartIndex = 0;
+    ScopedVclPtr<VirtualDevice> pVDev = createColorDevice();
+
     if (!rNamePrefix.empty())
     {
         for (const auto& rColor : rColorSet)
         {
-            auto pColorVDev = createColorVirtualDevice(rColor);
-            Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+            drawColor(*pVDev, rColor);
+            Bitmap aBmp = pVDev->GetBitmap(Point(), 
pVDev->GetOutputSizePixel());
             OUString sName = OUString::Concat(rNamePrefix) + 
OUString::number(nStartIndex);
             OUString sId = OUString::number(nStartIndex);
             pIconView.insert(nStartIndex, &sName, &sId, &aBmp, nullptr);
@@ -83,8 +86,8 @@ void SvxColorIconView::addEntriesForColorSet(weld::IconView& 
pIconView,
     {
         for (const auto& rColor : rColorSet)
         {
-            auto pColorVDev = createColorVirtualDevice(rColor);
-            Bitmap aBmp = pColorVDev->GetBitmap(Point(), 
pColorVDev->GetOutputSizePixel());
+            drawColor(*pVDev, rColor);
+            Bitmap aBmp = pVDev->GetBitmap(Point(), 
pVDev->GetOutputSizePixel());
             OUString sId = OUString::number(nStartIndex);
             OUString sName = u""_ustr;
             pIconView.insert(nStartIndex, &sName, &sId, &aBmp, nullptr);
@@ -93,25 +96,28 @@ void 
SvxColorIconView::addEntriesForColorSet(weld::IconView& pIconView,
     }
 }
 
-ScopedVclPtr<VirtualDevice> SvxColorIconView::createColorVirtualDevice(const 
Color& rColor)
+VclPtr<VirtualDevice> SvxColorIconView::createColorDevice()
 {
     const sal_uInt32 nEdgeLength = getEntryEdgeLength() - 2;
     VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create();
-    pVDev->SetOutputSizePixel(Size(nEdgeLength, nEdgeLength));
-
-    // Fill with the color
-    pVDev->SetFillColor(rColor);
-    pVDev->SetLineColor(COL_BLACK);
-    pVDev->DrawRect(tools::Rectangle(Point(0, 0), Size(nEdgeLength, 
nEdgeLength)));
+    const sal_Int32 nScaleFactor = pVDev->GetDPIScaleFactor();
+    const sal_uInt32 nScaledEdge = nEdgeLength * nScaleFactor;
+    pVDev->SetOutputSizePixel(Size(nScaledEdge, nScaledEdge));
+    return pVDev;
+}
 
-    Bitmap aPreviewBitmap = pVDev->GetBitmap(Point(0, 0), Size(nEdgeLength, 
nEdgeLength));
-    const Point aNull(0, 0);
-    if (pVDev->GetDPIScaleFactor() > 1)
-        aPreviewBitmap.Scale(pVDev->GetDPIScaleFactor(), 
pVDev->GetDPIScaleFactor());
-    const Size aSize(aPreviewBitmap.GetSizePixel());
-    pVDev->SetOutputSizePixel(aSize);
-    pVDev->DrawBitmap(aNull, aPreviewBitmap);
+void SvxColorIconView::drawColor(VirtualDevice& rDev, const Color& rColor)
+{
+    const Size aSize = rDev.GetOutputSizePixel();
+    rDev.SetFillColor(rColor);
+    rDev.SetLineColor(COL_BLACK);
+    rDev.DrawRect(tools::Rectangle(Point(0, 0), aSize));
+}
 
+ScopedVclPtr<VirtualDevice> SvxColorIconView::createColorVirtualDevice(const 
Color& rColor)
+{
+    VclPtr<VirtualDevice> pVDev = createColorDevice();
+    drawColor(*pVDev, rColor);
     return pVDev;
 }
 

Reply via email to