vcl/skia/SkiaHelper.cxx |   26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

New commits:
commit 45956f73bad4bb894f4e97c795700599ee281b54
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Sat Jul 12 22:53:44 2025 -0400
Commit:     Patrick Luby <guibomac...@gmail.com>
CommitDate: Sun Jul 13 19:53:05 2025 +0200

    tdf#166994 Don't remove image from cache too quickly
    
    While an animation is running, the same images are drawn in a
    repeating cycle but each frame is removed from the cache before
    it is drawn again.
    
    Blending a bitmap with an alpha channel can be very slow so
    it is much faster to keep all of the frames in the image cache.
    So, allow the image cache to temporarily increase in size by
    deferring removal of an image if it was cached within the last
    few seconds.
    
    Change-Id: I24d4b6a630671374227b60d6ee274443aee2332e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187790
    Reviewed-by: Patrick Luby <guibomac...@gmail.com>
    Tested-by: Jenkins

diff --git a/vcl/skia/SkiaHelper.cxx b/vcl/skia/SkiaHelper.cxx
index 98b44c722f27..c23583f3350a 100644
--- a/vcl/skia/SkiaHelper.cxx
+++ b/vcl/skia/SkiaHelper.cxx
@@ -666,6 +666,7 @@ struct ImageCacheItem
     OString key;
     sk_sp<SkImage> image;
     tools::Long size; // cost of the item
+    long long keepInCacheUntilMilliseconds; // don't remove from cache before 
this timestamp
 };
 } //namespace
 
@@ -675,6 +676,11 @@ struct ImageCacheItem
 static std::list<ImageCacheItem> imageCache;
 static tools::Long imageCacheSize = 0; // sum of all ImageCacheItem.size
 
+// Related: tdf#166994 set the minimum amount of time that an image must
+// remain in the image cache to 5 seconds. 2 seconds appears to be enough
+// on macOS but set it to 5 seconds just to be safe.
+const long long imageMinKeepInCacheMilliseconds = 5000;
+
 void addCachedImage(const OString& key, sk_sp<SkImage> image)
 {
     static bool disabled = getenv("SAL_DISABLE_SKIA_CACHE") != nullptr;
@@ -682,13 +688,28 @@ void addCachedImage(const OString& key, sk_sp<SkImage> 
image)
         return;
     tools::Long size = static_cast<tools::Long>(image->width()) * 
image->height()
                        * 
SkColorTypeBytesPerPixel(image->imageInfo().colorType());
-    imageCache.push_front({ key, image, size });
+    auto time = std::chrono::steady_clock::now().time_since_epoch();
+    auto now = 
std::chrono::duration_cast<std::chrono::milliseconds>(time).count();
+    imageCache.push_front({ key, image, size, now + 
imageMinKeepInCacheMilliseconds });
     imageCacheSize += size;
     SAL_INFO("vcl.skia.trace", "addcachedimage " << image << " :" << size << 
"/" << imageCacheSize);
     const tools::Long maxSize = maxImageCacheSize();
     while (imageCacheSize > maxSize)
     {
         assert(!imageCache.empty());
+
+        // tdf#166994 Don't remove image from cache too quickly
+        // While an animation is running, the same images are drawn in a
+        // repeating cycle but each frame is removed from the cache before
+        // it is drawn again.
+        // Blending a bitmap with an alpha channel can be very slow so
+        // it is much faster to keep all of the frames in the image cache.
+        // So, allow the image cache to temporarily increase in size by
+        // deferring removal of an image if it was cached within the last
+        // few seconds.
+        if (now < imageCache.back().keepInCacheUntilMilliseconds)
+            break;
+
         imageCacheSize -= imageCache.back().size;
         SAL_INFO("vcl.skia.trace",
                  "least used removal " << imageCache.back().image << ":" << 
imageCache.back().size);
@@ -702,6 +723,9 @@ sk_sp<SkImage> findCachedImage(const OString& key)
     {
         if (it->key == key)
         {
+            auto time = std::chrono::steady_clock::now().time_since_epoch();
+            auto now = 
std::chrono::duration_cast<std::chrono::milliseconds>(time).count();
+            it->keepInCacheUntilMilliseconds = now + 
imageMinKeepInCacheMilliseconds;
             sk_sp<SkImage> ret = it->image;
             SAL_INFO("vcl.skia.trace", "findcachedimage " << key << " : " << 
it->image << " found");
             imageCache.splice(imageCache.begin(), imageCache, it);

Reply via email to