3
3
using System . Collections . Generic ;
4
4
using System . IO ;
5
5
using System . Linq ;
6
+ using System . Threading ;
6
7
using System . Threading . Tasks ;
7
8
using System . Windows . Media ;
8
9
using System . Windows . Media . Imaging ;
@@ -15,6 +16,7 @@ namespace Flow.Launcher.Infrastructure.Image
15
16
public static class ImageLoader
16
17
{
17
18
private static readonly ImageCache ImageCache = new ( ) ;
19
+ private static SemaphoreSlim storageLock { get ; } = new SemaphoreSlim ( 1 , 1 ) ;
18
20
private static BinaryStorage < Dictionary < ( string , bool ) , int > > _storage ;
19
21
private static readonly ConcurrentDictionary < string , string > GuidToKey = new ( ) ;
20
22
private static IImageHashGenerator _hashGenerator ;
@@ -25,24 +27,18 @@ public static class ImageLoader
25
27
public const int FullIconSize = 256 ;
26
28
27
29
28
- private static readonly string [ ] ImageExtensions =
29
- {
30
- ".png" , ".jpg" , ".jpeg" , ".gif" , ".bmp" , ".tiff" , ".ico"
31
- } ;
30
+ private static readonly string [ ] ImageExtensions = { ".png" , ".jpg" , ".jpeg" , ".gif" , ".bmp" , ".tiff" , ".ico" } ;
32
31
33
- public static void Initialize ( )
32
+ public static async Task InitializeAsync ( )
34
33
{
35
34
_storage = new BinaryStorage < Dictionary < ( string , bool ) , int > > ( "Image" ) ;
36
35
_hashGenerator = new ImageHashGenerator ( ) ;
37
36
38
- var usage = LoadStorageToConcurrentDictionary ( ) ;
37
+ var usage = await LoadStorageToConcurrentDictionaryAsync ( ) ;
39
38
40
39
ImageCache . Initialize ( usage . ToDictionary ( x => x . Key , x => x . Value ) ) ;
41
40
42
- foreach ( var icon in new [ ]
43
- {
44
- Constant . DefaultIcon , Constant . MissingImgIcon
45
- } )
41
+ foreach ( var icon in new [ ] { Constant . DefaultIcon , Constant . MissingImgIcon } )
46
42
{
47
43
ImageSource img = new BitmapImage ( new Uri ( icon ) ) ;
48
44
img . Freeze ( ) ;
@@ -58,29 +54,41 @@ await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async
58
54
await LoadAsync ( path , isFullImage ) ;
59
55
}
60
56
} ) ;
61
- Log . Info ( $ "|ImageLoader.Initialize|Number of preload images is <{ ImageCache . CacheSize ( ) } >, Images Number: { ImageCache . CacheSize ( ) } , Unique Items { ImageCache . UniqueImagesInCache ( ) } ") ;
57
+ Log . Info (
58
+ $ "|ImageLoader.Initialize|Number of preload images is <{ ImageCache . CacheSize ( ) } >, Images Number: { ImageCache . CacheSize ( ) } , Unique Items { ImageCache . UniqueImagesInCache ( ) } ") ;
62
59
} ) ;
63
60
}
64
61
65
- public static void Save ( )
62
+ public static async Task Save ( )
66
63
{
67
- lock ( _storage )
64
+ await storageLock . WaitAsync ( ) ;
65
+
66
+ try
68
67
{
69
- _storage . Save ( ImageCache . Data
68
+ _storage . SaveAsync ( ImageCache . Data
70
69
. ToDictionary (
71
70
x => x . Key ,
72
71
x => x . Value . usage ) ) ;
73
72
}
73
+ finally
74
+ {
75
+ storageLock . Release ( ) ;
76
+ }
74
77
}
75
78
76
- private static ConcurrentDictionary < ( string , bool ) , int > LoadStorageToConcurrentDictionary ( )
79
+ private static async Task < ConcurrentDictionary < ( string , bool ) , int > > LoadStorageToConcurrentDictionaryAsync ( )
77
80
{
78
- lock ( _storage )
81
+ await storageLock . WaitAsync ( ) ;
82
+ try
79
83
{
80
- var loaded = _storage . TryLoad ( new Dictionary < ( string , bool ) , int > ( ) ) ;
84
+ var loaded = await _storage . TryLoadAsync ( new Dictionary < ( string , bool ) , int > ( ) ) ;
81
85
82
86
return new ConcurrentDictionary < ( string , bool ) , int > ( loaded ) ;
83
87
}
88
+ finally
89
+ {
90
+ storageLock . Release ( ) ;
91
+ }
84
92
}
85
93
86
94
private class ImageResult
@@ -129,6 +137,7 @@ private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool
129
137
ImageCache [ path , loadFullImage ] = image ;
130
138
return new ImageResult ( image , ImageType . ImageFile ) ;
131
139
}
140
+
132
141
if ( path . StartsWith ( "data:" , StringComparison . OrdinalIgnoreCase ) )
133
142
{
134
143
var imageSource = new BitmapImage ( new Uri ( path ) ) ;
@@ -158,6 +167,7 @@ private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool
158
167
159
168
return imageResult ;
160
169
}
170
+
161
171
private static async Task < BitmapImage > LoadRemoteImageAsync ( bool loadFullImage , Uri uriResult )
162
172
{
163
173
// Download image from url
@@ -173,6 +183,7 @@ private static async Task<BitmapImage> LoadRemoteImageAsync(bool loadFullImage,
173
183
image . DecodePixelHeight = SmallIconSize ;
174
184
image . DecodePixelWidth = SmallIconSize ;
175
185
}
186
+
176
187
image . StreamSource = buffer ;
177
188
image . EndInit ( ) ;
178
189
image . StreamSource = null ;
@@ -188,8 +199,8 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
188
199
if ( Directory . Exists ( path ) )
189
200
{
190
201
/* Directories can also have thumbnails instead of shell icons.
191
- * Generating thumbnails for a bunch of folder results while scrolling
192
- * could have a big impact on performance and Flow.Launcher responsibility.
202
+ * Generating thumbnails for a bunch of folder results while scrolling
203
+ * could have a big impact on performance and Flow.Launcher responsibility.
193
204
* - Solution: just load the icon
194
205
*/
195
206
type = ImageType . Folder ;
@@ -208,9 +219,9 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
208
219
}
209
220
else
210
221
{
211
- /* Although the documentation for GetImage on MSDN indicates that
222
+ /* Although the documentation for GetImage on MSDN indicates that
212
223
* if a thumbnail is available it will return one, this has proved to not
213
- * be the case in many situations while testing.
224
+ * be the case in many situations while testing.
214
225
* - Solution: explicitly pass the ThumbnailOnly flag
215
226
*/
216
227
image = GetThumbnail ( path , ThumbnailOptions . ThumbnailOnly ) ;
@@ -236,7 +247,8 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
236
247
return new ImageResult ( image , type ) ;
237
248
}
238
249
239
- private static BitmapSource GetThumbnail ( string path , ThumbnailOptions option = ThumbnailOptions . ThumbnailOnly , int size = SmallIconSize )
250
+ private static BitmapSource GetThumbnail ( string path , ThumbnailOptions option = ThumbnailOptions . ThumbnailOnly ,
251
+ int size = SmallIconSize )
240
252
{
241
253
return WindowsThumbnailProvider . GetThumbnail (
242
254
path ,
@@ -261,17 +273,19 @@ public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullI
261
273
262
274
var img = imageResult . ImageSource ;
263
275
if ( imageResult . ImageType != ImageType . Error && imageResult . ImageType != ImageType . Cache )
264
- { // we need to get image hash
276
+ {
277
+ // we need to get image hash
265
278
string hash = EnableImageHash ? _hashGenerator . GetHashFromImage ( img ) : null ;
266
279
if ( hash != null )
267
280
{
268
-
269
281
if ( GuidToKey . TryGetValue ( hash , out string key ) )
270
- { // image already exists
282
+ {
283
+ // image already exists
271
284
img = ImageCache [ key , loadFullImage ] ?? img ;
272
285
}
273
286
else
274
- { // new guid
287
+ {
288
+ // new guid
275
289
276
290
GuidToKey [ hash ] = path ;
277
291
}
@@ -289,7 +303,7 @@ private static BitmapImage LoadFullImage(string path)
289
303
BitmapImage image = new BitmapImage ( ) ;
290
304
image . BeginInit ( ) ;
291
305
image . CacheOption = BitmapCacheOption . OnLoad ;
292
- image . UriSource = new Uri ( path ) ;
306
+ image . UriSource = new Uri ( path ) ;
293
307
image . CreateOptions = BitmapCreateOptions . IgnoreColorProfile ;
294
308
image . EndInit ( ) ;
295
309
@@ -314,8 +328,10 @@ private static BitmapImage LoadFullImage(string path)
314
328
resizedHeight . EndInit ( ) ;
315
329
return resizedHeight ;
316
330
}
331
+
317
332
return resizedWidth ;
318
333
}
334
+
319
335
return image ;
320
336
}
321
337
}
0 commit comments