33using System . Collections . Generic ;
44using System . IO ;
55using System . Linq ;
6+ using System . Threading ;
67using System . Threading . Tasks ;
78using System . Windows . Media ;
89using System . Windows . Media . Imaging ;
@@ -15,6 +16,7 @@ namespace Flow.Launcher.Infrastructure.Image
1516 public static class ImageLoader
1617 {
1718 private static readonly ImageCache ImageCache = new ( ) ;
19+ private static SemaphoreSlim storageLock { get ; } = new SemaphoreSlim ( 1 , 1 ) ;
1820 private static BinaryStorage < Dictionary < ( string , bool ) , int > > _storage ;
1921 private static readonly ConcurrentDictionary < string , string > GuidToKey = new ( ) ;
2022 private static IImageHashGenerator _hashGenerator ;
@@ -25,24 +27,18 @@ public static class ImageLoader
2527 public const int FullIconSize = 256 ;
2628
2729
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" } ;
3231
33- public static void Initialize ( )
32+ public static async Task InitializeAsync ( )
3433 {
3534 _storage = new BinaryStorage < Dictionary < ( string , bool ) , int > > ( "Image" ) ;
3635 _hashGenerator = new ImageHashGenerator ( ) ;
3736
38- var usage = LoadStorageToConcurrentDictionary ( ) ;
37+ var usage = await LoadStorageToConcurrentDictionaryAsync ( ) ;
3938
4039 ImageCache . Initialize ( usage . ToDictionary ( x => x . Key , x => x . Value ) ) ;
4140
42- foreach ( var icon in new [ ]
43- {
44- Constant . DefaultIcon , Constant . MissingImgIcon
45- } )
41+ foreach ( var icon in new [ ] { Constant . DefaultIcon , Constant . MissingImgIcon } )
4642 {
4743 ImageSource img = new BitmapImage ( new Uri ( icon ) ) ;
4844 img . Freeze ( ) ;
@@ -58,29 +54,41 @@ await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async
5854 await LoadAsync ( path , isFullImage ) ;
5955 }
6056 } ) ;
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 ( ) } ") ;
6259 } ) ;
6360 }
6461
65- public static void Save ( )
62+ public static async Task Save ( )
6663 {
67- lock ( _storage )
64+ await storageLock . WaitAsync ( ) ;
65+
66+ try
6867 {
69- _storage . Save ( ImageCache . Data
68+ _storage . SaveAsync ( ImageCache . Data
7069 . ToDictionary (
7170 x => x . Key ,
7271 x => x . Value . usage ) ) ;
7372 }
73+ finally
74+ {
75+ storageLock . Release ( ) ;
76+ }
7477 }
7578
76- private static ConcurrentDictionary < ( string , bool ) , int > LoadStorageToConcurrentDictionary ( )
79+ private static async Task < ConcurrentDictionary < ( string , bool ) , int > > LoadStorageToConcurrentDictionaryAsync ( )
7780 {
78- lock ( _storage )
81+ await storageLock . WaitAsync ( ) ;
82+ try
7983 {
80- var loaded = _storage . TryLoad ( new Dictionary < ( string , bool ) , int > ( ) ) ;
84+ var loaded = await _storage . TryLoadAsync ( new Dictionary < ( string , bool ) , int > ( ) ) ;
8185
8286 return new ConcurrentDictionary < ( string , bool ) , int > ( loaded ) ;
8387 }
88+ finally
89+ {
90+ storageLock . Release ( ) ;
91+ }
8492 }
8593
8694 private class ImageResult
@@ -129,6 +137,7 @@ private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool
129137 ImageCache [ path , loadFullImage ] = image ;
130138 return new ImageResult ( image , ImageType . ImageFile ) ;
131139 }
140+
132141 if ( path . StartsWith ( "data:" , StringComparison . OrdinalIgnoreCase ) )
133142 {
134143 var imageSource = new BitmapImage ( new Uri ( path ) ) ;
@@ -158,6 +167,7 @@ private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool
158167
159168 return imageResult ;
160169 }
170+
161171 private static async Task < BitmapImage > LoadRemoteImageAsync ( bool loadFullImage , Uri uriResult )
162172 {
163173 // Download image from url
@@ -173,6 +183,7 @@ private static async Task<BitmapImage> LoadRemoteImageAsync(bool loadFullImage,
173183 image . DecodePixelHeight = SmallIconSize ;
174184 image . DecodePixelWidth = SmallIconSize ;
175185 }
186+
176187 image . StreamSource = buffer ;
177188 image . EndInit ( ) ;
178189 image . StreamSource = null ;
@@ -188,8 +199,8 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
188199 if ( Directory . Exists ( path ) )
189200 {
190201 /* 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.
193204 * - Solution: just load the icon
194205 */
195206 type = ImageType . Folder ;
@@ -208,9 +219,9 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
208219 }
209220 else
210221 {
211- /* Although the documentation for GetImage on MSDN indicates that
222+ /* Although the documentation for GetImage on MSDN indicates that
212223 * 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.
214225 * - Solution: explicitly pass the ThumbnailOnly flag
215226 */
216227 image = GetThumbnail ( path , ThumbnailOptions . ThumbnailOnly ) ;
@@ -236,7 +247,8 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
236247 return new ImageResult ( image , type ) ;
237248 }
238249
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 )
240252 {
241253 return WindowsThumbnailProvider . GetThumbnail (
242254 path ,
@@ -261,17 +273,19 @@ public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullI
261273
262274 var img = imageResult . ImageSource ;
263275 if ( imageResult . ImageType != ImageType . Error && imageResult . ImageType != ImageType . Cache )
264- { // we need to get image hash
276+ {
277+ // we need to get image hash
265278 string hash = EnableImageHash ? _hashGenerator . GetHashFromImage ( img ) : null ;
266279 if ( hash != null )
267280 {
268-
269281 if ( GuidToKey . TryGetValue ( hash , out string key ) )
270- { // image already exists
282+ {
283+ // image already exists
271284 img = ImageCache [ key , loadFullImage ] ?? img ;
272285 }
273286 else
274- { // new guid
287+ {
288+ // new guid
275289
276290 GuidToKey [ hash ] = path ;
277291 }
@@ -289,7 +303,7 @@ private static BitmapImage LoadFullImage(string path)
289303 BitmapImage image = new BitmapImage ( ) ;
290304 image . BeginInit ( ) ;
291305 image . CacheOption = BitmapCacheOption . OnLoad ;
292- image . UriSource = new Uri ( path ) ;
306+ image . UriSource = new Uri ( path ) ;
293307 image . CreateOptions = BitmapCreateOptions . IgnoreColorProfile ;
294308 image . EndInit ( ) ;
295309
@@ -314,8 +328,10 @@ private static BitmapImage LoadFullImage(string path)
314328 resizedHeight . EndInit ( ) ;
315329 return resizedHeight ;
316330 }
331+
317332 return resizedWidth ;
318333 }
334+
319335 return image ;
320336 }
321337 }
0 commit comments