mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-14 03:37:10 +08:00
Refactoring image cache
use parallel linq to preload images, should be faster
This commit is contained in:
parent
80f31f75ad
commit
03051a95cf
@ -84,7 +84,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Resource\FontHelper.cs" />
|
<Compile Include="Resource\FontHelper.cs" />
|
||||||
<Compile Include="Resource\Theme.cs" />
|
<Compile Include="Resource\Theme.cs" />
|
||||||
<Compile Include="UserSettings\PluginConfig.cs" />
|
<Compile Include="UserSettings\PluginSettings.cs" />
|
||||||
<Compile Include="UserSettings\PluginHotkey.cs" />
|
<Compile Include="UserSettings\PluginHotkey.cs" />
|
||||||
<Compile Include="UserSettings\Settings.cs" />
|
<Compile Include="UserSettings\Settings.cs" />
|
||||||
<Compile Include="Updater\SemanticVersion.cs" />
|
<Compile Include="Updater\SemanticVersion.cs" />
|
||||||
|
@ -23,7 +23,7 @@ namespace Wox
|
|||||||
{
|
{
|
||||||
private const string Unique = "Wox_Unique_Application_Mutex";
|
private const string Unique = "Wox_Unique_Application_Mutex";
|
||||||
public static MainWindow Window { get; private set; }
|
public static MainWindow Window { get; private set; }
|
||||||
|
public static ImageLoader.ImageLoader ImageLoader;
|
||||||
public static PublicAPIInstance API { get; private set; }
|
public static PublicAPIInstance API { get; private set; }
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
@ -48,7 +48,9 @@ namespace Wox
|
|||||||
|
|
||||||
ThreadPool.SetMaxThreads(30, 10);
|
ThreadPool.SetMaxThreads(30, 10);
|
||||||
ThreadPool.SetMinThreads(10, 5);
|
ThreadPool.SetMinThreads(10, 5);
|
||||||
ThreadPool.QueueUserWorkItem(_ => { ImageLoader.ImageLoader.PreloadImages(); });
|
|
||||||
|
ImageLoader = new ImageLoader.ImageLoader();
|
||||||
|
ThreadPool.QueueUserWorkItem(_ => { ImageLoader.PreloadImages(); });
|
||||||
|
|
||||||
PluginManager.Initialize();
|
PluginManager.Initialize();
|
||||||
|
|
||||||
|
@ -13,8 +13,7 @@ namespace Wox.Converters
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return App.ImageLoader.Load(value.ToString());
|
||||||
return ImageLoader.ImageLoader.Load(value.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Wox.Infrastructure.Storage;
|
using Wox.Infrastructure.Storage;
|
||||||
@ -8,39 +9,26 @@ namespace Wox.ImageLoader
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class ImageCache
|
public class ImageCache
|
||||||
{
|
{
|
||||||
private int counter;
|
private const int MaxCached = 200;
|
||||||
private const int maxCached = 200;
|
public ConcurrentDictionary<string, int> TopUsedImages = new ConcurrentDictionary<string, int>();
|
||||||
public Dictionary<string, int> TopUsedImages = new Dictionary<string, int>();
|
|
||||||
|
|
||||||
public void Add(string path)
|
public void Add(string path)
|
||||||
{
|
{
|
||||||
if (TopUsedImages.ContainsKey(path))
|
if (TopUsedImages.ContainsKey(path))
|
||||||
{
|
{
|
||||||
TopUsedImages[path] = TopUsedImages[path] + 1 ;
|
TopUsedImages[path] = TopUsedImages[path] + 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TopUsedImages.Add(path, 1);
|
TopUsedImages[path] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TopUsedImages.Count > maxCached)
|
if (TopUsedImages.Count > MaxCached)
|
||||||
{
|
{
|
||||||
TopUsedImages = TopUsedImages.OrderByDescending(o => o.Value)
|
var images = TopUsedImages.OrderByDescending(o => o.Value)
|
||||||
.Take(maxCached)
|
.Take(MaxCached)
|
||||||
.ToDictionary(i => i.Key, i => i.Value);
|
.ToDictionary(i => i.Key, i => i.Value);
|
||||||
}
|
TopUsedImages = new ConcurrentDictionary<string, int>(images);
|
||||||
|
|
||||||
if (++counter == 30)
|
|
||||||
{
|
|
||||||
counter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(string path)
|
|
||||||
{
|
|
||||||
if (TopUsedImages.ContainsKey(path))
|
|
||||||
{
|
|
||||||
TopUsedImages.Remove(path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -7,6 +8,7 @@ using System.Windows;
|
|||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Linq;
|
||||||
using Wox.Infrastructure;
|
using Wox.Infrastructure;
|
||||||
using Wox.Infrastructure.Storage;
|
using Wox.Infrastructure.Storage;
|
||||||
|
|
||||||
@ -14,9 +16,9 @@ namespace Wox.ImageLoader
|
|||||||
{
|
{
|
||||||
public class ImageLoader
|
public class ImageLoader
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, ImageSource> ImageSources = new Dictionary<string, ImageSource>();
|
private readonly ConcurrentDictionary<string, ImageSource> ImageSources = new ConcurrentDictionary<string, ImageSource>();
|
||||||
|
|
||||||
private static readonly List<string> imageExts = new List<string>
|
private readonly List<string> ImageExts = new List<string>
|
||||||
{
|
{
|
||||||
".png",
|
".png",
|
||||||
".jpg",
|
".jpg",
|
||||||
@ -27,7 +29,7 @@ namespace Wox.ImageLoader
|
|||||||
".ico"
|
".ico"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly List<string> selfExts = new List<string>
|
private readonly List<string> SelfExts = new List<string>
|
||||||
{
|
{
|
||||||
".exe",
|
".exe",
|
||||||
".lnk",
|
".lnk",
|
||||||
@ -37,10 +39,10 @@ namespace Wox.ImageLoader
|
|||||||
".appref-ms"
|
".appref-ms"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly ImageCache _cache;
|
private readonly ImageCache _cache;
|
||||||
private static readonly BinaryStorage<ImageCache> _storage;
|
private readonly BinaryStorage<ImageCache> _storage;
|
||||||
|
|
||||||
static ImageLoader()
|
public ImageLoader()
|
||||||
{
|
{
|
||||||
_storage = new BinaryStorage<ImageCache>();
|
_storage = new BinaryStorage<ImageCache>();
|
||||||
_cache = _storage.Load();
|
_cache = _storage.Load();
|
||||||
@ -51,7 +53,7 @@ namespace Wox.ImageLoader
|
|||||||
_storage.Save();
|
_storage.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImageSource GetIcon(string fileName)
|
private ImageSource GetIcon(string fileName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -68,32 +70,28 @@ namespace Wox.ImageLoader
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PreloadImages()
|
public void PreloadImages()
|
||||||
{
|
{
|
||||||
//ImageCacheStroage.Instance.TopUsedImages can be changed during foreach, so we need to make a copy
|
Stopwatch.Debug($"Preload {_cache.TopUsedImages.Count} images", () =>
|
||||||
var imageList = new Dictionary<string, int>(_cache.TopUsedImages);
|
|
||||||
Stopwatch.Debug($"Preload {imageList.Count} images", () =>
|
|
||||||
{
|
{
|
||||||
foreach (var image in imageList)
|
_cache.TopUsedImages.AsParallel().Where(i => !ImageSources.ContainsKey(i.Key)).ForAll(i =>
|
||||||
{
|
{
|
||||||
if (!ImageSources.ContainsKey(image.Key))
|
var img = Load(i.Key, false);
|
||||||
{
|
if (img != null)
|
||||||
ImageSource img = Load(image.Key, false);
|
{
|
||||||
if (img != null)
|
// todo happlebao magic
|
||||||
{
|
// the image created on other threads can be accessed from main ui thread,
|
||||||
img.Freeze(); //to make it copy to UI thread
|
// this line made it possible
|
||||||
if (!ImageSources.ContainsKey(image.Key))
|
// should be changed the Dispatcher.InvokeAsync in the future
|
||||||
{
|
img.Freeze();
|
||||||
KeyValuePair<string, int> copyedImg = image;
|
|
||||||
ImageSources.Add(copyedImg.Key, img);
|
ImageSources[i.Key] = img;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageSource Load(string path, bool addToCache = true)
|
public ImageSource Load(string path, bool addToCache = true)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(path)) return null;
|
if (string.IsNullOrEmpty(path)) return null;
|
||||||
ImageSource img = null;
|
ImageSource img = null;
|
||||||
@ -118,21 +116,20 @@ namespace Wox.ImageLoader
|
|||||||
{
|
{
|
||||||
img = new BitmapImage(new Uri(path));
|
img = new BitmapImage(new Uri(path));
|
||||||
}
|
}
|
||||||
else if (selfExts.Contains(ext) && File.Exists(path))
|
else if (SelfExts.Contains(ext) && File.Exists(path))
|
||||||
{
|
{
|
||||||
img = GetIcon(path);
|
img = GetIcon(path);
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrEmpty(path) && imageExts.Contains(ext) && File.Exists(path))
|
else if (!string.IsNullOrEmpty(path) && ImageExts.Contains(ext) && File.Exists(path))
|
||||||
{
|
{
|
||||||
img = new BitmapImage(new Uri(path));
|
img = new BitmapImage(new Uri(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (img != null && addToCache)
|
if (img != null && addToCache)
|
||||||
{
|
{
|
||||||
if (!ImageSources.ContainsKey(path))
|
if (!ImageSources.ContainsKey(path))
|
||||||
{
|
{
|
||||||
ImageSources.Add(path, img);
|
ImageSources[path] = img;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +138,7 @@ namespace Wox.ImageLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// http://blogs.msdn.com/b/oldnewthing/archive/2011/01/27/10120844.aspx
|
// http://blogs.msdn.com/b/oldnewthing/archive/2011/01/27/10120844.aspx
|
||||||
private static Icon GetFileIcon(string name)
|
private Icon GetFileIcon(string name)
|
||||||
{
|
{
|
||||||
SHFILEINFO shfi = new SHFILEINFO();
|
SHFILEINFO shfi = new SHFILEINFO();
|
||||||
uint flags = SHGFI_SYSICONINDEX;
|
uint flags = SHGFI_SYSICONINDEX;
|
||||||
|
@ -550,7 +550,7 @@ namespace Wox
|
|||||||
pluginAuthor.Text = InternationalizationManager.Instance.GetTranslation("author") + ": " + pair.Metadata.Author;
|
pluginAuthor.Text = InternationalizationManager.Instance.GetTranslation("author") + ": " + pair.Metadata.Author;
|
||||||
pluginSubTitle.Text = pair.Metadata.Description;
|
pluginSubTitle.Text = pair.Metadata.Description;
|
||||||
pluginId = pair.Metadata.ID;
|
pluginId = pair.Metadata.ID;
|
||||||
pluginIcon.Source = ImageLoader.ImageLoader.Load(pair.Metadata.FullIcoPath);
|
pluginIcon.Source = App.ImageLoader.Load(pair.Metadata.FullIcoPath);
|
||||||
|
|
||||||
var customizedPluginConfig = _settings.PluginSettings[pluginId];
|
var customizedPluginConfig = _settings.PluginSettings[pluginId];
|
||||||
cbDisablePlugin.IsChecked = customizedPluginConfig != null && customizedPluginConfig.Disabled;
|
cbDisablePlugin.IsChecked = customizedPluginConfig != null && customizedPluginConfig.Disabled;
|
||||||
|
@ -125,7 +125,7 @@
|
|||||||
<Compile Include="Converters\StringNullOrEmptyToVisibilityConverter.cs" />
|
<Compile Include="Converters\StringNullOrEmptyToVisibilityConverter.cs" />
|
||||||
<Compile Include="Helper\VisibilityExtensions.cs" />
|
<Compile Include="Helper\VisibilityExtensions.cs" />
|
||||||
<Compile Include="Helper\SingletonWindowOpener.cs" />
|
<Compile Include="Helper\SingletonWindowOpener.cs" />
|
||||||
<Compile Include="ImageLoader\ImageCacheStroage.cs" />
|
<Compile Include="ImageLoader\ImageCache.cs" />
|
||||||
<Compile Include="NotifyIconManager.cs" />
|
<Compile Include="NotifyIconManager.cs" />
|
||||||
<Compile Include="PublicAPIInstance.cs" />
|
<Compile Include="PublicAPIInstance.cs" />
|
||||||
<Compile Include="ResultListBox.xaml.cs">
|
<Compile Include="ResultListBox.xaml.cs">
|
||||||
|
Loading…
Reference in New Issue
Block a user