From b14d6c9216209db195530e3b0aee67a4657dac13 Mon Sep 17 00:00:00 2001
From: AT <14300910+theClueless@users.noreply.github.com>
Date: Fri, 3 Jan 2020 21:16:17 +0200
Subject: [PATCH 1/6] adding hash ability to image loader (reducing the load on
memory)
---
Wox.Infrastructure/Image/ImageCache.cs | 13 +++
.../Image/ImageHashGenerator.cs | 47 +++++++++
Wox.Infrastructure/Image/ImageLoader.cs | 99 +++++++++++++++----
Wox.Infrastructure/Image/ThumbnailReader.cs | 1 -
Wox.Infrastructure/Wox.Infrastructure.csproj | 1 +
5 files changed, 141 insertions(+), 20 deletions(-)
create mode 100644 Wox.Infrastructure/Image/ImageHashGenerator.cs
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..96361d8155
--- /dev/null
+++ b/Wox.Infrastructure/Image/ImageHashGenerator.cs
@@ -0,0 +1,47 @@
+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();
+ enc.Frames.Add(BitmapFrame.Create(image));
+ 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..8479ae94a6 100644
--- a/Wox.Infrastructure/Image/ImageLoader.cs
+++ b/Wox.Infrastructure/Image/ImageLoader.cs
@@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
@@ -14,6 +15,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 +33,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 +47,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().Where(i => !ImageCache.ContainsKey(i.Key)).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 +61,54 @@ 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));
+ return new ImageResult(new BitmapImage(new Uri(path)), 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 +117,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 +145,7 @@ namespace Wox.Infrastructure.Image
}
else
{
+ type = ImageType.File;
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
Constant.ThumbnailSize, ThumbnailOptions.None);
}
@@ -128,17 +155,51 @@ 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)
+ {
+ // return LoadInternal(path, loadFullImage).ImageSource;
+ 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 @@
+
From 72e1a19ea5df36a09012ce8236423f5ada33bb91 Mon Sep 17 00:00:00 2001
From: AT <14300910+theClueless@users.noreply.github.com>
Date: Fri, 3 Jan 2020 21:16:38 +0200
Subject: [PATCH 2/6] remove using
---
Wox.Infrastructure/Image/ImageLoader.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/Wox.Infrastructure/Image/ImageLoader.cs b/Wox.Infrastructure/Image/ImageLoader.cs
index 8479ae94a6..e4ff51cd5e 100644
--- a/Wox.Infrastructure/Image/ImageLoader.cs
+++ b/Wox.Infrastructure/Image/ImageLoader.cs
@@ -2,7 +2,6 @@
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
From e80147d24e2e47af4f5d22fd31c26faa06b56f7f Mon Sep 17 00:00:00 2001
From: AT <14300910+theClueless@users.noreply.github.com>
Date: Fri, 3 Jan 2020 21:17:58 +0200
Subject: [PATCH 3/6] removed a duplicate check
---
Wox.Infrastructure/Image/ImageLoader.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Wox.Infrastructure/Image/ImageLoader.cs b/Wox.Infrastructure/Image/ImageLoader.cs
index e4ff51cd5e..761613bc49 100644
--- a/Wox.Infrastructure/Image/ImageLoader.cs
+++ b/Wox.Infrastructure/Image/ImageLoader.cs
@@ -46,7 +46,7 @@ namespace Wox.Infrastructure.Image
{
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
{
- ImageCache.Usage.AsParallel().Where(i => !ImageCache.ContainsKey(i.Key)).ForAll(x =>
+ ImageCache.Usage.AsParallel().ForAll(x =>
{
Load(x.Key);
});
From 05bd32f750c4bb9f8b4f670cf661bb8b235dfe56 Mon Sep 17 00:00:00 2001
From: AT <14300910+theClueless@users.noreply.github.com>
Date: Fri, 3 Jan 2020 22:01:15 +0200
Subject: [PATCH 4/6] remove comment
---
Wox.Infrastructure/Image/ImageLoader.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/Wox.Infrastructure/Image/ImageLoader.cs b/Wox.Infrastructure/Image/ImageLoader.cs
index 761613bc49..59a7a24fb4 100644
--- a/Wox.Infrastructure/Image/ImageLoader.cs
+++ b/Wox.Infrastructure/Image/ImageLoader.cs
@@ -174,7 +174,6 @@ namespace Wox.Infrastructure.Image
public static ImageSource Load(string path, bool loadFullImage = false)
{
- // return LoadInternal(path, loadFullImage).ImageSource;
var imageResult = LoadInternal(path, loadFullImage);
var img = imageResult.ImageSource;
From fd59088528481d25615579a8a32968b057c6f6c2 Mon Sep 17 00:00:00 2001
From: AT <14300910+theClueless@users.noreply.github.com>
Date: Fri, 3 Jan 2020 22:33:00 +0200
Subject: [PATCH 5/6] made data images freeze as well
---
Wox.Infrastructure/Image/ImageLoader.cs | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Wox.Infrastructure/Image/ImageLoader.cs b/Wox.Infrastructure/Image/ImageLoader.cs
index 59a7a24fb4..528900ce7c 100644
--- a/Wox.Infrastructure/Image/ImageLoader.cs
+++ b/Wox.Infrastructure/Image/ImageLoader.cs
@@ -100,7 +100,9 @@ namespace Wox.Infrastructure.Image
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
- return new ImageResult(new BitmapImage(new Uri(path)), ImageType.Data);
+ var imageSource = new BitmapImage(new Uri(path));
+ imageSource.Freeze();
+ return new ImageResult(imageSource, ImageType.Data);
}
if (!Path.IsPathRooted(path))
@@ -181,7 +183,7 @@ namespace Wox.Infrastructure.Image
{ // 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];
@@ -195,7 +197,7 @@ namespace Wox.Infrastructure.Image
// update cache
ImageCache[path] = img;
}
-
+
return img;
}
From 28b098cfb7361ad1ab72f2bcae98ee7a53dc0fec Mon Sep 17 00:00:00 2001
From: AT <14300910+theClueless@users.noreply.github.com>
Date: Sat, 4 Jan 2020 00:40:37 +0200
Subject: [PATCH 6/6] make image created in hash freeze
---
Wox.Infrastructure/Image/ImageHashGenerator.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Wox.Infrastructure/Image/ImageHashGenerator.cs b/Wox.Infrastructure/Image/ImageHashGenerator.cs
index 96361d8155..9ace8b74fc 100644
--- a/Wox.Infrastructure/Image/ImageHashGenerator.cs
+++ b/Wox.Infrastructure/Image/ImageHashGenerator.cs
@@ -27,7 +27,9 @@ namespace Wox.Infrastructure.Image
// enc2.Frames.Add(BitmapFrame.Create(tt));
var enc = new JpegBitmapEncoder();
- enc.Frames.Add(BitmapFrame.Create(image));
+ var bitmapFrame = BitmapFrame.Create(image);
+ bitmapFrame.Freeze();
+ enc.Frames.Add(bitmapFrame);
enc.Save(outStream);
var byteArray = outStream.GetBuffer();
using (var sha1 = new SHA1CryptoServiceProvider())