From c0999bca37a2042e405ae08b55d65b7dd78aa5e8 Mon Sep 17 00:00:00 2001 From: Jaume Pujantell Date: Thu, 24 Sep 2026 14:55:14 +0000 Subject: [PATCH] vcl: remember graphic size after a threaded load An Impress master slide with a bitmap background could turn plain blue after saving and reopening the document. The saved styles.xml still named the fill (for example draw:fill-image-name="Bitmap_20_2") but held no draw:fill-image for it, so the reload fell back to the default fill colour. The document had to stay idle long enough for its images to be swapped out, which made the problem look intermittent. Switching slides prefetches the page graphics with GraphicFilter::MakeGraphicsAvailableThreaded, added in commit 8062e88e73ac ("try to make available all slide images using threads") with Change-Id Ifc39a2757834a9fb0dbafa61f13f5454e69af330. A text object without a fill reports the page background there, so the master background image is part of that prefetch. The loader hands the decoded result to ImpGraphic::updateFromLoadedGraphic directly, which resets mnSizeBytes to 0 and leaves the recalculation to swapIn, but swapIn never runs on this path. The next memory trim then swapped the graphic out with a size of 0. A swapped out graphic that is not prepared answers GetSizeBytes from that value, so SvxUnoBitmapTable treated the fill as empty and left it out of the export. updateFromLoadedGraphic now measures the loaded content while it is in memory, so the graphic keeps its size through the next swap out whichever path loaded it, and the memory manager counts the real size. Signed-off-by: Jaume Pujantell Change-Id: Id472f542a531a1b9578322f3fdd12a8dd39843df Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13475 Tested-by: Jenkins CPCI Reviewed-by: Miklos Vajna (cherry picked from commit 6a19f87f2430feacd16715947d385cee7e7c5a65) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211533 Reviewed-by: Xisco Fauli Tested-by: Jenkins --- vcl/qa/cppunit/GraphicTest.cxx | 19 +++++++++++++++++++ vcl/source/gdi/impgraph.cxx | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/vcl/qa/cppunit/GraphicTest.cxx b/vcl/qa/cppunit/GraphicTest.cxx index b4c3ca6f1c2d..ab43554e4009 100644 --- a/vcl/qa/cppunit/GraphicTest.cxx +++ b/vcl/qa/cppunit/GraphicTest.cxx @@ -1359,6 +1359,25 @@ CPPUNIT_TEST_FIXTURE(GraphicTest, testColorChangeToTransparent) rBitmapAfter.GetPixelColor(410, 140)); } +CPPUNIT_TEST_FIXTURE(GraphicTest, testSwappedOutGraphicKeepsSizeAfterThreadedLoad) +{ + // Given a PNG graphic that was loaded once and then swapped out: + Graphic aGraphic = makeUnloadedGraphic(u"png"); + CPPUNIT_ASSERT(aGraphic.makeAvailable()); + CPPUNIT_ASSERT(aGraphic.ImplGetImpGraphic()->swapOut()); + + // When it is loaded again by the threaded loader and swapped out once more: + std::vector aGraphics{ &aGraphic }; + GraphicFilter::GetGraphicFilter().MakeGraphicsAvailableThreaded(aGraphics); + CPPUNIT_ASSERT(aGraphic.isAvailable()); + CPPUNIT_ASSERT(aGraphic.ImplGetImpGraphic()->swapOut()); + + // Then it still reports its size, so it does not look like an empty graphic. + // Without the fix in place, this test would have failed, the graphic reported a size of 0 + // bytes. + CPPUNIT_ASSERT(aGraphic.GetSizeBytes() > 0); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx index 03801668c2d4..32078ac09d6c 100644 --- a/vcl/source/gdi/impgraph.cxx +++ b/vcl/source/gdi/impgraph.cxx @@ -1412,6 +1412,10 @@ void ImpGraphic::updateFromLoadedGraphic(const ImpGraphic* pGraphic) mbSwapOut = false; } + + // The loaded content is measured while it is in memory, so the graphic still knows its size + // after it is swapped out again. + ensureCurrentSizeInBytes(); } void ImpGraphic::dumpState(rtl::OStringBuffer &rState)