/ src / modules / launcher / Wox.Infrastructure / Image / ImageCache.cs
ImageCache.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System;
  6  using System.Collections.Concurrent;
  7  using System.Collections.Generic;
  8  using System.Linq;
  9  using System.Windows.Media;
 10  
 11  using Wox.Infrastructure.Storage;
 12  using Wox.Plugin;
 13  using Wox.Plugin.Logger;
 14  
 15  namespace Wox.Infrastructure.Image
 16  {
 17      [Serializable]
 18      public class ImageCache
 19      {
 20          private const int MaxCached = 50;
 21          private const int PermissibleFactor = 2;
 22  
 23          private readonly ConcurrentDictionary<string, ImageSource> _data = new ConcurrentDictionary<string, ImageSource>();
 24          private readonly WoxJsonStorage<ConcurrentDictionary<string, int>> _storage;
 25  
 26          public ConcurrentDictionary<string, int> Usage { get; private set; } = new ConcurrentDictionary<string, int>();
 27  
 28          public ImageCache()
 29          {
 30              _storage = new WoxJsonStorage<ConcurrentDictionary<string, int>>("ImageCache");
 31              Usage = _storage.Load();
 32          }
 33  
 34          public ImageSource this[string path]
 35          {
 36              get
 37              {
 38                  Usage.AddOrUpdate(path, 1, (k, v) => v + 1);
 39                  _data.TryGetValue(path, out ImageSource i);
 40  
 41                  if (i == null)
 42                  {
 43                      Log.Warn($"ImageSource is null for path: {path}", GetType());
 44                  }
 45  
 46                  return i;
 47              }
 48  
 49              set
 50              {
 51                  _data[path] = value;
 52  
 53                  // To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size
 54                  // This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time
 55                  if (_data.Count > PermissibleFactor * MaxCached)
 56                  {
 57                      // This function resizes the Usage dictionary, taking the top 'maxCached' number of items and filtering the image icons that are not accessed frequently.
 58                      Cleanup();
 59  
 60                      // To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
 61                      foreach (var key in _data.Keys)
 62                      {
 63                          // Using Ordinal since this is internal
 64                          if (!Usage.TryGetValue(key, out _) && !(key.Equals(Constant.ErrorIcon, StringComparison.Ordinal) || key.Equals(Constant.LightThemedErrorIcon, StringComparison.Ordinal)))
 65                          {
 66                              _data.TryRemove(key, out _);
 67                          }
 68                      }
 69                  }
 70              }
 71          }
 72  
 73          public void Cleanup()
 74          {
 75              var images = Usage
 76                  .OrderByDescending(o => o.Value)
 77                  .Take(MaxCached)
 78                  .ToDictionary(i => i.Key, i => i.Value);
 79              Usage = new ConcurrentDictionary<string, int>(images);
 80          }
 81  
 82          public bool ContainsKey(string key)
 83          {
 84              var contains = _data.ContainsKey(key);
 85              return contains;
 86          }
 87  
 88          public int CacheSize()
 89          {
 90              return _data.Count;
 91          }
 92  
 93          /// <summary>
 94          /// return the number of unique images in the cache (by reference not by checking images content)
 95          /// </summary>
 96          public int UniqueImagesInCache()
 97          {
 98              return _data.Values.Distinct().Count();
 99          }
100  
101          public Dictionary<string, int> GetUsageAsDictionary()
102          {
103              return new Dictionary<string, int>(Usage);
104          }
105  
106          public void Save()
107          {
108              _storage.Save();
109          }
110      }
111  }