@@ -14,6 +14,8 @@ public static class ImageLoader
14
14
{
15
15
private static readonly ImageCache ImageCache = new ImageCache ( ) ;
16
16
private static BinaryStorage < ConcurrentDictionary < string , int > > _storage ;
17
+ private static readonly ConcurrentDictionary < string , string > GuidToKey = new ConcurrentDictionary < string , string > ( ) ;
18
+ private static IImageHashGenerator _hashGenerator ;
17
19
18
20
19
21
private static readonly string [ ] ImageExtensions =
@@ -30,7 +32,8 @@ public static class ImageLoader
30
32
31
33
public static void Initialize ( )
32
34
{
33
- _storage = new BinaryStorage < ConcurrentDictionary < string , int > > ( "Image" ) ;
35
+ _storage = new BinaryStorage < ConcurrentDictionary < string , int > > ( "Image" ) ;
36
+ _hashGenerator = new ImageHashGenerator ( ) ;
34
37
ImageCache . Usage = _storage . TryLoad ( new ConcurrentDictionary < string , int > ( ) ) ;
35
38
36
39
foreach ( var icon in new [ ] { Constant . DefaultIcon , Constant . ErrorIcon } )
@@ -43,16 +46,12 @@ public static void Initialize()
43
46
{
44
47
Stopwatch . Normal ( "|ImageLoader.Initialize|Preload images cost" , ( ) =>
45
48
{
46
- ImageCache . Usage . AsParallel ( ) . Where ( i => ! ImageCache . ContainsKey ( i . Key ) ) . ForAll ( i =>
49
+ ImageCache . Usage . AsParallel ( ) . ForAll ( x =>
47
50
{
48
- var img = Load ( i . Key ) ;
49
- if ( img != null )
50
- {
51
- ImageCache [ i . Key ] = img ;
52
- }
51
+ Load ( x . Key ) ;
53
52
} ) ;
54
53
} ) ;
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 ( ) } ") ;
56
55
} ) ;
57
56
}
58
57
@@ -61,31 +60,56 @@ public static void Save()
61
60
ImageCache . Cleanup ( ) ;
62
61
_storage . Save ( ImageCache . Usage ) ;
63
62
}
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 )
66
87
{
67
88
ImageSource image ;
89
+ ImageType type = ImageType . Error ;
68
90
try
69
91
{
70
92
if ( string . IsNullOrEmpty ( path ) )
71
93
{
72
- return ImageCache [ Constant . ErrorIcon ] ;
94
+ return new ImageResult ( ImageCache [ Constant . ErrorIcon ] , ImageType . Error ) ;
73
95
}
74
96
if ( ImageCache . ContainsKey ( path ) )
75
97
{
76
- return ImageCache [ path ] ;
98
+ return new ImageResult ( ImageCache [ path ] , ImageType . Cache ) ;
77
99
}
78
-
100
+
79
101
if ( path . StartsWith ( "data:" , StringComparison . OrdinalIgnoreCase ) )
80
102
{
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 ) ;
82
106
}
83
107
84
108
if ( ! Path . IsPathRooted ( path ) )
85
109
{
86
110
path = Path . Combine ( Constant . ProgramDirectory , "Images" , Path . GetFileName ( path ) ) ;
87
111
}
88
-
112
+
89
113
if ( Directory . Exists ( path ) )
90
114
{
91
115
/* Directories can also have thumbnails instead of shell icons.
@@ -94,14 +118,17 @@ public static ImageSource Load(string path, bool loadFullImage = false)
94
118
* Wox responsibility.
95
119
* - Solution: just load the icon
96
120
*/
121
+ type = ImageType . Folder ;
97
122
image = WindowsThumbnailProvider . GetThumbnail ( path , Constant . ThumbnailSize ,
98
123
Constant . ThumbnailSize , ThumbnailOptions . IconOnly ) ;
124
+
99
125
}
100
126
else if ( File . Exists ( path ) )
101
127
{
102
128
var extension = Path . GetExtension ( path ) . ToLower ( ) ;
103
129
if ( ImageExtensions . Contains ( extension ) )
104
130
{
131
+ type = ImageType . ImageFile ;
105
132
if ( loadFullImage )
106
133
{
107
134
image = LoadFullImage ( path ) ;
@@ -119,6 +146,7 @@ public static ImageSource Load(string path, bool loadFullImage = false)
119
146
}
120
147
else
121
148
{
149
+ type = ImageType . File ;
122
150
image = WindowsThumbnailProvider . GetThumbnail ( path , Constant . ThumbnailSize ,
123
151
Constant . ThumbnailSize , ThumbnailOptions . None ) ;
124
152
}
@@ -128,17 +156,50 @@ public static ImageSource Load(string path, bool loadFullImage = false)
128
156
image = ImageCache [ Constant . ErrorIcon ] ;
129
157
path = Constant . ErrorIcon ;
130
158
}
131
- ImageCache [ path ] = image ;
132
- image . Freeze ( ) ;
159
+
160
+ if ( type != ImageType . Error )
161
+ {
162
+ image . Freeze ( ) ;
163
+ }
133
164
}
134
165
catch ( System . Exception e )
135
166
{
136
167
Log . Exception ( $ "|ImageLoader.Load|Failed to get thumbnail for { path } ", e ) ;
137
-
168
+ type = ImageType . Error ;
138
169
image = ImageCache [ Constant . ErrorIcon ] ;
139
170
ImageCache [ path ] = image ;
140
171
}
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 ;
142
203
}
143
204
144
205
private static BitmapImage LoadFullImage ( string path )
0 commit comments