diff --git a/Wox.Infrastructure/Image/ImageCache.cs b/Wox.Infrastructure/Image/ImageCache.cs
index 599b69066d..5e74a2a380 100644
--- a/Wox.Infrastructure/Image/ImageCache.cs
+++ b/Wox.Infrastructure/Image/ImageCache.cs
@@ -39,6 +39,19 @@ namespace Wox.Infrastructure.Image
var contains = _data.ContainsKey(key);
return contains;
}
+
+ public int CacheSize()
+ {
+ return _data.Count;
+ }
+
+ ///
+ /// return the number of unique images in the cache (by reference not by checking images content)
+ ///
+ public int UniqueImagesInCache()
+ {
+ return _data.Values.Distinct().Count();
+ }
}
}
diff --git a/Wox.Infrastructure/Image/ImageHashGenerator.cs b/Wox.Infrastructure/Image/ImageHashGenerator.cs
new file mode 100644
index 0000000000..9ace8b74fc
--- /dev/null
+++ b/Wox.Infrastructure/Image/ImageHashGenerator.cs
@@ -0,0 +1,49 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace Wox.Infrastructure.Image
+{
+ public interface IImageHashGenerator
+ {
+ string GetHashFromImage(ImageSource image);
+ }
+ public class ImageHashGenerator : IImageHashGenerator
+ {
+ public string GetHashFromImage(ImageSource imageSource)
+ {
+ if (!(imageSource is BitmapSource image))
+ {
+ return null;
+ }
+
+ try
+ {
+ using (var outStream = new MemoryStream())
+ {
+ // PngBitmapEncoder enc2 = new PngBitmapEncoder();
+ // enc2.Frames.Add(BitmapFrame.Create(tt));
+
+ var enc = new JpegBitmapEncoder();
+ var bitmapFrame = BitmapFrame.Create(image);
+ bitmapFrame.Freeze();
+ enc.Frames.Add(bitmapFrame);
+ enc.Save(outStream);
+ var byteArray = outStream.GetBuffer();
+ using (var sha1 = new SHA1CryptoServiceProvider())
+ {
+ var hash = Convert.ToBase64String(sha1.ComputeHash(byteArray));
+ return hash;
+ }
+ }
+ }
+ catch
+ {
+ return null;
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wox.Infrastructure/Image/ImageLoader.cs b/Wox.Infrastructure/Image/ImageLoader.cs
index 3498e4f3b2..528900ce7c 100644
--- a/Wox.Infrastructure/Image/ImageLoader.cs
+++ b/Wox.Infrastructure/Image/ImageLoader.cs
@@ -14,6 +14,8 @@ namespace Wox.Infrastructure.Image
{
private static readonly ImageCache ImageCache = new ImageCache();
private static BinaryStorage> _storage;
+ private static readonly ConcurrentDictionary GuidToKey = new ConcurrentDictionary();
+ private static IImageHashGenerator _hashGenerator;
private static readonly string[] ImageExtensions =
@@ -30,7 +32,8 @@ namespace Wox.Infrastructure.Image
public static void Initialize()
{
- _storage = new BinaryStorage> ("Image");
+ _storage = new BinaryStorage>("Image");
+ _hashGenerator = new ImageHashGenerator();
ImageCache.Usage = _storage.TryLoad(new ConcurrentDictionary());
foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon })
@@ -43,16 +46,12 @@ namespace Wox.Infrastructure.Image
{
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
{
- ImageCache.Usage.AsParallel().Where(i => !ImageCache.ContainsKey(i.Key)).ForAll(i =>
+ ImageCache.Usage.AsParallel().ForAll(x =>
{
- var img = Load(i.Key);
- if (img != null)
- {
- ImageCache[i.Key] = img;
- }
+ Load(x.Key);
});
});
- Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>");
+ Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
});
}
@@ -61,31 +60,56 @@ namespace Wox.Infrastructure.Image
ImageCache.Cleanup();
_storage.Save(ImageCache.Usage);
}
-
- public static ImageSource Load(string path, bool loadFullImage = false)
+
+ private class ImageResult
+ {
+ public ImageResult(ImageSource imageSource, ImageType imageType)
+ {
+ ImageSource = imageSource;
+ ImageType = imageType;
+ }
+
+ public ImageType ImageType { get; }
+ public ImageSource ImageSource { get; }
+ }
+
+ private enum ImageType
+ {
+ File,
+ Folder,
+ Data,
+ ImageFile,
+ Error,
+ Cache
+ }
+
+ private static ImageResult LoadInternal(string path, bool loadFullImage = false)
{
ImageSource image;
+ ImageType type = ImageType.Error;
try
{
if (string.IsNullOrEmpty(path))
{
- return ImageCache[Constant.ErrorIcon];
+ return new ImageResult(ImageCache[Constant.ErrorIcon], ImageType.Error);
}
if (ImageCache.ContainsKey(path))
{
- return ImageCache[path];
+ return new ImageResult(ImageCache[path], ImageType.Cache);
}
-
+
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
- return new BitmapImage(new Uri(path));
+ var imageSource = new BitmapImage(new Uri(path));
+ imageSource.Freeze();
+ return new ImageResult(imageSource, ImageType.Data);
}
if (!Path.IsPathRooted(path))
{
path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path));
}
-
+
if (Directory.Exists(path))
{
/* Directories can also have thumbnails instead of shell icons.
@@ -94,14 +118,17 @@ namespace Wox.Infrastructure.Image
* Wox responsibility.
* - Solution: just load the icon
*/
+ type = ImageType.Folder;
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
Constant.ThumbnailSize, ThumbnailOptions.IconOnly);
+
}
else if (File.Exists(path))
{
var extension = Path.GetExtension(path).ToLower();
if (ImageExtensions.Contains(extension))
{
+ type = ImageType.ImageFile;
if (loadFullImage)
{
image = LoadFullImage(path);
@@ -119,6 +146,7 @@ namespace Wox.Infrastructure.Image
}
else
{
+ type = ImageType.File;
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
Constant.ThumbnailSize, ThumbnailOptions.None);
}
@@ -128,17 +156,50 @@ namespace Wox.Infrastructure.Image
image = ImageCache[Constant.ErrorIcon];
path = Constant.ErrorIcon;
}
- ImageCache[path] = image;
- image.Freeze();
+
+ if (type != ImageType.Error)
+ {
+ image.Freeze();
+ }
}
catch (System.Exception e)
{
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path}", e);
-
+ type = ImageType.Error;
image = ImageCache[Constant.ErrorIcon];
ImageCache[path] = image;
}
- return image;
+ return new ImageResult(image, type);
+ }
+
+ private static bool EnableImageHash = true;
+
+ public static ImageSource Load(string path, bool loadFullImage = false)
+ {
+ var imageResult = LoadInternal(path, loadFullImage);
+
+ var img = imageResult.ImageSource;
+ if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
+ { // we need to get image hash
+ string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
+ if (hash != null)
+ {
+ if (GuidToKey.TryGetValue(hash, out string key))
+ { // image already exists
+ img = ImageCache[key];
+ }
+ else
+ { // new guid
+ GuidToKey[hash] = path;
+ }
+ }
+
+ // update cache
+ ImageCache[path] = img;
+ }
+
+
+ return img;
}
private static BitmapImage LoadFullImage(string path)
diff --git a/Wox.Infrastructure/Image/ThumbnailReader.cs b/Wox.Infrastructure/Image/ThumbnailReader.cs
index e0ea9bba35..bd65fc7000 100644
--- a/Wox.Infrastructure/Image/ThumbnailReader.cs
+++ b/Wox.Infrastructure/Image/ThumbnailReader.cs
@@ -110,7 +110,6 @@ namespace Wox.Infrastructure.Image
try
{
-
return Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
finally
diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj
index af76894ed4..bd14c56034 100644
--- a/Wox.Infrastructure/Wox.Infrastructure.csproj
+++ b/Wox.Infrastructure/Wox.Infrastructure.csproj
@@ -71,6 +71,7 @@
+