mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
refactor Image Cache to single dictionary
This commit is contained in:
parent
d3d69be9c2
commit
bf271b9da7
2 changed files with 37 additions and 37 deletions
|
|
@ -11,61 +11,65 @@ namespace Flow.Launcher.Infrastructure.Image
|
||||||
public class ImageCache
|
public class ImageCache
|
||||||
{
|
{
|
||||||
private const int MaxCached = 50;
|
private const int MaxCached = 50;
|
||||||
public ConcurrentDictionary<string, int> Usage = new ConcurrentDictionary<string, int>();
|
public ConcurrentDictionary<string, (int usage, ImageSource imageSource)> Data { get; private set; } = new ConcurrentDictionary<string, (int, ImageSource)>();
|
||||||
private readonly ConcurrentDictionary<string, ImageSource> _data = new ConcurrentDictionary<string, ImageSource>();
|
|
||||||
private const int permissibleFactor = 2;
|
private const int permissibleFactor = 2;
|
||||||
|
|
||||||
|
public void Initialization(Dictionary<string, int> usage)
|
||||||
|
{
|
||||||
|
foreach (var key in usage.Keys)
|
||||||
|
{
|
||||||
|
Data[key] = (usage[key], null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ImageSource this[string path]
|
public ImageSource this[string path]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Usage.AddOrUpdate(path, 1, (k, v) => v + 1);
|
if (Data.TryGetValue(path, out var value))
|
||||||
var i = _data[path];
|
{
|
||||||
return i;
|
value.usage++;
|
||||||
|
return value.imageSource;
|
||||||
|
}
|
||||||
|
else return null;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_data[path] = value;
|
Data.AddOrUpdate(path, (1, value), (k, v) =>
|
||||||
|
{
|
||||||
|
v.imageSource = value;
|
||||||
|
v.usage++;
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
// This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time
|
// This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time
|
||||||
if (_data.Count > permissibleFactor * MaxCached)
|
if (Data.Count > permissibleFactor * MaxCached)
|
||||||
{
|
{
|
||||||
// This function resizes the Usage dictionary, taking the top 'maxCached' number of items and filtering the image icons that are not accessed frequently.
|
|
||||||
Cleanup();
|
|
||||||
|
|
||||||
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
|
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
|
||||||
foreach (var key in _data.Keys)
|
|
||||||
|
|
||||||
|
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
|
||||||
{
|
{
|
||||||
int dictValue;
|
if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
|
||||||
if (!Usage.TryGetValue(key, out dictValue) && !(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
|
|
||||||
{
|
{
|
||||||
ImageSource imgSource;
|
Data.TryRemove(key, out _);
|
||||||
_data.TryRemove(key, out imgSource);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup()
|
|
||||||
{
|
|
||||||
var images = Usage
|
|
||||||
.OrderByDescending(o => o.Value)
|
|
||||||
.Take(MaxCached)
|
|
||||||
.ToDictionary(i => i.Key, i => i.Value);
|
|
||||||
Usage = new ConcurrentDictionary<string, int>(images);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContainsKey(string key)
|
public bool ContainsKey(string key)
|
||||||
{
|
{
|
||||||
var contains = _data.ContainsKey(key);
|
var contains = Data.ContainsKey(key);
|
||||||
return contains;
|
return contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CacheSize()
|
public int CacheSize()
|
||||||
{
|
{
|
||||||
return _data.Count;
|
return Data.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -73,7 +77,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int UniqueImagesInCache()
|
public int UniqueImagesInCache()
|
||||||
{
|
{
|
||||||
return _data.Values.Distinct().Count();
|
return Data.Values.Select(x => x.imageSource).Distinct().Count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
||||||
_storage = new BinaryStorage<Dictionary<string, int>>("Image");
|
_storage = new BinaryStorage<Dictionary<string, int>>("Image");
|
||||||
_hashGenerator = new ImageHashGenerator();
|
_hashGenerator = new ImageHashGenerator();
|
||||||
|
|
||||||
ImageCache.Usage = LoadStorageToConcurrentDictionary();
|
var usage = LoadStorageToConcurrentDictionary();
|
||||||
|
|
||||||
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
|
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
|
||||||
{
|
{
|
||||||
|
|
@ -48,12 +48,12 @@ namespace Flow.Launcher.Infrastructure.Image
|
||||||
{
|
{
|
||||||
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
|
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
|
||||||
{
|
{
|
||||||
ImageCache.Usage.AsParallel().ForAll(x =>
|
ImageCache.Data.AsParallel().ForAll(x =>
|
||||||
{
|
{
|
||||||
Load(x.Key);
|
Load(x.Key);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
|
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,14 +61,13 @@ namespace Flow.Launcher.Infrastructure.Image
|
||||||
{
|
{
|
||||||
lock (_storage)
|
lock (_storage)
|
||||||
{
|
{
|
||||||
ImageCache.Cleanup();
|
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage));
|
||||||
_storage.Save(new Dictionary<string, int>(ImageCache.Usage));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConcurrentDictionary<string, int> LoadStorageToConcurrentDictionary()
|
private static ConcurrentDictionary<string, int> LoadStorageToConcurrentDictionary()
|
||||||
{
|
{
|
||||||
lock(_storage)
|
lock (_storage)
|
||||||
{
|
{
|
||||||
var loaded = _storage.TryLoad(new Dictionary<string, int>());
|
var loaded = _storage.TryLoad(new Dictionary<string, int>());
|
||||||
|
|
||||||
|
|
@ -222,13 +221,10 @@ namespace Flow.Launcher.Infrastructure.Image
|
||||||
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
|
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
|
||||||
if (hash != null)
|
if (hash != null)
|
||||||
{
|
{
|
||||||
int ImageCacheValue;
|
|
||||||
if (GuidToKey.TryGetValue(hash, out string key))
|
if (GuidToKey.TryGetValue(hash, out string key))
|
||||||
{ // image already exists
|
{ // image already exists
|
||||||
if (ImageCache.Usage.TryGetValue(path, out ImageCacheValue))
|
img = ImageCache[key] ?? img;
|
||||||
{
|
|
||||||
img = ImageCache[key];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ // new guid
|
{ // new guid
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue