Skip to content

Commit 4cf3cff

Browse files
authored
Merge pull request #115 from theClueless/imageOptimization
Imageoptimization
2 parents c63e8d3 + 28b098c commit 4cf3cff

File tree

5 files changed

+143
-20
lines changed

5 files changed

+143
-20
lines changed

Wox.Infrastructure/Image/ImageCache.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ public bool ContainsKey(string key)
3939
var contains = _data.ContainsKey(key);
4040
return contains;
4141
}
42+
43+
public int CacheSize()
44+
{
45+
return _data.Count;
46+
}
47+
48+
/// <summary>
49+
/// return the number of unique images in the cache (by reference not by checking images content)
50+
/// </summary>
51+
public int UniqueImagesInCache()
52+
{
53+
return _data.Values.Distinct().Count();
54+
}
4255
}
4356

4457
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.IO;
3+
using System.Security.Cryptography;
4+
using System.Windows.Media;
5+
using System.Windows.Media.Imaging;
6+
7+
namespace Wox.Infrastructure.Image
8+
{
9+
public interface IImageHashGenerator
10+
{
11+
string GetHashFromImage(ImageSource image);
12+
}
13+
public class ImageHashGenerator : IImageHashGenerator
14+
{
15+
public string GetHashFromImage(ImageSource imageSource)
16+
{
17+
if (!(imageSource is BitmapSource image))
18+
{
19+
return null;
20+
}
21+
22+
try
23+
{
24+
using (var outStream = new MemoryStream())
25+
{
26+
// PngBitmapEncoder enc2 = new PngBitmapEncoder();
27+
// enc2.Frames.Add(BitmapFrame.Create(tt));
28+
29+
var enc = new JpegBitmapEncoder();
30+
var bitmapFrame = BitmapFrame.Create(image);
31+
bitmapFrame.Freeze();
32+
enc.Frames.Add(bitmapFrame);
33+
enc.Save(outStream);
34+
var byteArray = outStream.GetBuffer();
35+
using (var sha1 = new SHA1CryptoServiceProvider())
36+
{
37+
var hash = Convert.ToBase64String(sha1.ComputeHash(byteArray));
38+
return hash;
39+
}
40+
}
41+
}
42+
catch
43+
{
44+
return null;
45+
}
46+
47+
}
48+
}
49+
}

Wox.Infrastructure/Image/ImageLoader.cs

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public static class ImageLoader
1414
{
1515
private static readonly ImageCache ImageCache = new ImageCache();
1616
private static BinaryStorage<ConcurrentDictionary<string, int>> _storage;
17+
private static readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
18+
private static IImageHashGenerator _hashGenerator;
1719

1820

1921
private static readonly string[] ImageExtensions =
@@ -30,7 +32,8 @@ public static class ImageLoader
3032

3133
public static void Initialize()
3234
{
33-
_storage = new BinaryStorage<ConcurrentDictionary<string, int>> ("Image");
35+
_storage = new BinaryStorage<ConcurrentDictionary<string, int>>("Image");
36+
_hashGenerator = new ImageHashGenerator();
3437
ImageCache.Usage = _storage.TryLoad(new ConcurrentDictionary<string, int>());
3538

3639
foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon })
@@ -43,16 +46,12 @@ public static void Initialize()
4346
{
4447
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
4548
{
46-
ImageCache.Usage.AsParallel().Where(i => !ImageCache.ContainsKey(i.Key)).ForAll(i =>
49+
ImageCache.Usage.AsParallel().ForAll(x =>
4750
{
48-
var img = Load(i.Key);
49-
if (img != null)
50-
{
51-
ImageCache[i.Key] = img;
52-
}
51+
Load(x.Key);
5352
});
5453
});
55-
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>");
54+
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
5655
});
5756
}
5857

@@ -61,31 +60,56 @@ public static void Save()
6160
ImageCache.Cleanup();
6261
_storage.Save(ImageCache.Usage);
6362
}
64-
65-
public static ImageSource Load(string path, bool loadFullImage = false)
63+
64+
private class ImageResult
65+
{
66+
public ImageResult(ImageSource imageSource, ImageType imageType)
67+
{
68+
ImageSource = imageSource;
69+
ImageType = imageType;
70+
}
71+
72+
public ImageType ImageType { get; }
73+
public ImageSource ImageSource { get; }
74+
}
75+
76+
private enum ImageType
77+
{
78+
File,
79+
Folder,
80+
Data,
81+
ImageFile,
82+
Error,
83+
Cache
84+
}
85+
86+
private static ImageResult LoadInternal(string path, bool loadFullImage = false)
6687
{
6788
ImageSource image;
89+
ImageType type = ImageType.Error;
6890
try
6991
{
7092
if (string.IsNullOrEmpty(path))
7193
{
72-
return ImageCache[Constant.ErrorIcon];
94+
return new ImageResult(ImageCache[Constant.ErrorIcon], ImageType.Error);
7395
}
7496
if (ImageCache.ContainsKey(path))
7597
{
76-
return ImageCache[path];
98+
return new ImageResult(ImageCache[path], ImageType.Cache);
7799
}
78-
100+
79101
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
80102
{
81-
return new BitmapImage(new Uri(path));
103+
var imageSource = new BitmapImage(new Uri(path));
104+
imageSource.Freeze();
105+
return new ImageResult(imageSource, ImageType.Data);
82106
}
83107

84108
if (!Path.IsPathRooted(path))
85109
{
86110
path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path));
87111
}
88-
112+
89113
if (Directory.Exists(path))
90114
{
91115
/* Directories can also have thumbnails instead of shell icons.
@@ -94,14 +118,17 @@ public static ImageSource Load(string path, bool loadFullImage = false)
94118
* Wox responsibility.
95119
* - Solution: just load the icon
96120
*/
121+
type = ImageType.Folder;
97122
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
98123
Constant.ThumbnailSize, ThumbnailOptions.IconOnly);
124+
99125
}
100126
else if (File.Exists(path))
101127
{
102128
var extension = Path.GetExtension(path).ToLower();
103129
if (ImageExtensions.Contains(extension))
104130
{
131+
type = ImageType.ImageFile;
105132
if (loadFullImage)
106133
{
107134
image = LoadFullImage(path);
@@ -119,6 +146,7 @@ public static ImageSource Load(string path, bool loadFullImage = false)
119146
}
120147
else
121148
{
149+
type = ImageType.File;
122150
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
123151
Constant.ThumbnailSize, ThumbnailOptions.None);
124152
}
@@ -128,17 +156,50 @@ public static ImageSource Load(string path, bool loadFullImage = false)
128156
image = ImageCache[Constant.ErrorIcon];
129157
path = Constant.ErrorIcon;
130158
}
131-
ImageCache[path] = image;
132-
image.Freeze();
159+
160+
if (type != ImageType.Error)
161+
{
162+
image.Freeze();
163+
}
133164
}
134165
catch (System.Exception e)
135166
{
136167
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path}", e);
137-
168+
type = ImageType.Error;
138169
image = ImageCache[Constant.ErrorIcon];
139170
ImageCache[path] = image;
140171
}
141-
return image;
172+
return new ImageResult(image, type);
173+
}
174+
175+
private static bool EnableImageHash = true;
176+
177+
public static ImageSource Load(string path, bool loadFullImage = false)
178+
{
179+
var imageResult = LoadInternal(path, loadFullImage);
180+
181+
var img = imageResult.ImageSource;
182+
if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
183+
{ // we need to get image hash
184+
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
185+
if (hash != null)
186+
{
187+
if (GuidToKey.TryGetValue(hash, out string key))
188+
{ // image already exists
189+
img = ImageCache[key];
190+
}
191+
else
192+
{ // new guid
193+
GuidToKey[hash] = path;
194+
}
195+
}
196+
197+
// update cache
198+
ImageCache[path] = img;
199+
}
200+
201+
202+
return img;
142203
}
143204

144205
private static BitmapImage LoadFullImage(string path)

Wox.Infrastructure/Image/ThumbnailReader.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ public static BitmapSource GetThumbnail(string fileName, int width, int height,
110110

111111
try
112112
{
113-
114113
return Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
115114
}
116115
finally

Wox.Infrastructure/Wox.Infrastructure.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<Compile Include="Hotkey\InterceptKeys.cs" />
7272
<Compile Include="Hotkey\KeyEvent.cs" />
7373
<Compile Include="Image\ImageCache.cs" />
74+
<Compile Include="Image\ImageHashGenerator.cs" />
7475
<Compile Include="Image\ImageLoader.cs" />
7576
<Compile Include="Image\ThumbnailReader.cs" />
7677
<Compile Include="Logger\Log.cs" />

0 commit comments

Comments
 (0)