@@ -13,12 +13,13 @@ namespace Flow.Launcher.Infrastructure.Image
13
13
{
14
14
public static class ImageLoader
15
15
{
16
- private static readonly ImageCache ImageCache = new ImageCache ( ) ;
16
+ private static readonly ImageCache _imageCache = new ImageCache ( ) ;
17
+ private static readonly ConcurrentDictionary < string , string > _guidToKey = new ConcurrentDictionary < string , string > ( ) ;
18
+ private static readonly bool _enableHashImage = true ;
19
+
17
20
private static BinaryStorage < Dictionary < string , int > > _storage ;
18
- private static readonly ConcurrentDictionary < string , string > GuidToKey = new ConcurrentDictionary < string , string > ( ) ;
19
21
private static IImageHashGenerator _hashGenerator ;
20
22
21
-
22
23
private static readonly string [ ] ImageExtensions =
23
24
{
24
25
".png" ,
@@ -30,38 +31,38 @@ public static class ImageLoader
30
31
".ico"
31
32
} ;
32
33
33
-
34
34
public static void Initialize ( )
35
35
{
36
36
_storage = new BinaryStorage < Dictionary < string , int > > ( "Image" ) ;
37
37
_hashGenerator = new ImageHashGenerator ( ) ;
38
38
39
- ImageCache . Usage = LoadStorageToConcurrentDictionary ( ) ;
39
+ _imageCache . Usage = LoadStorageToConcurrentDictionary ( ) ;
40
40
41
41
foreach ( var icon in new [ ] { Constant . DefaultIcon , Constant . ErrorIcon } )
42
42
{
43
43
ImageSource img = new BitmapImage ( new Uri ( icon ) ) ;
44
44
img . Freeze ( ) ;
45
- ImageCache [ icon ] = img ;
45
+ _imageCache [ icon ] = img ;
46
46
}
47
+
47
48
Task . Run ( ( ) =>
48
49
{
49
50
Stopwatch . Normal ( "|ImageLoader.Initialize|Preload images cost" , ( ) =>
50
51
{
51
- ImageCache . Usage . AsParallel ( ) . ForAll ( x =>
52
+ _imageCache . Usage . AsParallel ( ) . ForAll ( x =>
52
53
{
53
54
Load ( x . Key ) ;
54
55
} ) ;
55
56
} ) ;
56
- Log . Info ( $ "|ImageLoader.Initialize|Number of preload images is <{ ImageCache . Usage . Count } >, Images Number: { ImageCache . CacheSize ( ) } , Unique Items { ImageCache . UniqueImagesInCache ( ) } ") ;
57
+ Log . Info ( $ "|ImageLoader.Initialize|Number of preload images is <{ _imageCache . Usage . Count } >, Images Number: { _imageCache . CacheSize ( ) } , Unique Items { _imageCache . UniqueImagesInCache ( ) } ") ;
57
58
} ) ;
58
59
}
59
60
60
61
public static void Save ( )
61
62
{
62
63
lock ( _storage )
63
64
{
64
- _storage . Save ( ImageCache . CleanupAndToDictionary ( ) ) ;
65
+ _storage . Save ( _imageCache . CleanupAndToDictionary ( ) ) ;
65
66
}
66
67
}
67
68
@@ -99,17 +100,17 @@ private enum ImageType
99
100
100
101
private static ImageResult LoadInternal ( string path , bool loadFullImage = false )
101
102
{
102
- ImageSource image ;
103
- ImageType type = ImageType . Error ;
103
+ ImageResult imageResult ;
104
+
104
105
try
105
106
{
106
107
if ( string . IsNullOrEmpty ( path ) )
107
108
{
108
- return new ImageResult ( ImageCache [ Constant . ErrorIcon ] , ImageType . Error ) ;
109
+ return new ImageResult ( _imageCache [ Constant . ErrorIcon ] , ImageType . Error ) ;
109
110
}
110
- if ( ImageCache . ContainsKey ( path ) )
111
+ if ( _imageCache . ContainsKey ( path ) )
111
112
{
112
- return new ImageResult ( ImageCache [ path ] , ImageType . Cache ) ;
113
+ return new ImageResult ( _imageCache [ path ] , ImageType . Cache ) ;
113
114
}
114
115
115
116
if ( path . StartsWith ( "data:" , StringComparison . OrdinalIgnoreCase ) )
@@ -124,95 +125,121 @@ private static ImageResult LoadInternal(string path, bool loadFullImage = false)
124
125
path = Path . Combine ( Constant . ProgramDirectory , "Images" , Path . GetFileName ( path ) ) ;
125
126
}
126
127
127
- if ( Directory . Exists ( path ) )
128
+ imageResult = GetThumbnailResult ( ref path , loadFullImage ) ;
129
+ }
130
+ catch ( System . Exception e )
131
+ {
132
+ try
133
+ {
134
+ // Get thumbnail may fail for certain images on the first try, retry again has proven to work
135
+ imageResult = GetThumbnailResult ( ref path , loadFullImage ) ;
136
+ }
137
+ catch ( System . Exception e2 )
128
138
{
129
- /* Directories can also have thumbnails instead of shell icons.
130
- * Generating thumbnails for a bunch of folders while scrolling through
131
- * results from Everything makes a big impact on performance and
132
- * Flow.Launcher responsibility.
133
- * - Solution: just load the icon
134
- */
135
- type = ImageType . Folder ;
136
- image = WindowsThumbnailProvider . GetThumbnail ( path , Constant . ThumbnailSize ,
137
- Constant . ThumbnailSize , ThumbnailOptions . IconOnly ) ;
139
+ Log . Exception ( $ "|ImageLoader.Load|Failed to get thumbnail for { path } on first try", e ) ;
140
+ Log . Exception ( $ "|ImageLoader.Load|Failed to get thumbnail for { path } on second try", e2 ) ;
138
141
142
+ ImageSource image = _imageCache [ Constant . ErrorIcon ] ;
143
+ _imageCache [ path ] = image ;
144
+ imageResult = new ImageResult ( image , ImageType . Error ) ;
139
145
}
140
- else if ( File . Exists ( path ) )
146
+ }
147
+
148
+ return imageResult ;
149
+ }
150
+
151
+ private static ImageResult GetThumbnailResult ( ref string path , bool loadFullImage = false )
152
+ {
153
+ ImageSource image ;
154
+ ImageType type = ImageType . Error ;
155
+
156
+ if ( Directory . Exists ( path ) )
157
+ {
158
+ /* Directories can also have thumbnails instead of shell icons.
159
+ * Generating thumbnails for a bunch of folders while scrolling through
160
+ * results from Everything makes a big impact on performance and
161
+ * Flow.Launcher responsibility.
162
+ * - Solution: just load the icon
163
+ */
164
+ type = ImageType . Folder ;
165
+ image = GetThumbnail ( path , ThumbnailOptions . IconOnly ) ;
166
+ }
167
+ else if ( File . Exists ( path ) )
168
+ {
169
+ var extension = Path . GetExtension ( path ) . ToLower ( ) ;
170
+ if ( ImageExtensions . Contains ( extension ) )
141
171
{
142
- var extension = Path . GetExtension ( path ) . ToLower ( ) ;
143
- if ( ImageExtensions . Contains ( extension ) )
172
+ type = ImageType . ImageFile ;
173
+ if ( loadFullImage )
144
174
{
145
- type = ImageType . ImageFile ;
146
- if ( loadFullImage )
147
- {
148
- image = LoadFullImage ( path ) ;
149
- }
150
- else
151
- {
152
- /* Although the documentation for GetImage on MSDN indicates that
153
- * if a thumbnail is available it will return one, this has proved to not
154
- * be the case in many situations while testing.
155
- * - Solution: explicitly pass the ThumbnailOnly flag
156
- */
157
- image = WindowsThumbnailProvider . GetThumbnail ( path , Constant . ThumbnailSize ,
158
- Constant . ThumbnailSize , ThumbnailOptions . ThumbnailOnly ) ;
159
- }
175
+ image = LoadFullImage ( path ) ;
160
176
}
161
177
else
162
178
{
163
- type = ImageType . File ;
164
- image = WindowsThumbnailProvider . GetThumbnail ( path , Constant . ThumbnailSize ,
165
- Constant . ThumbnailSize , ThumbnailOptions . None ) ;
179
+ /* Although the documentation for GetImage on MSDN indicates that
180
+ * if a thumbnail is available it will return one, this has proved to not
181
+ * be the case in many situations while testing.
182
+ * - Solution: explicitly pass the ThumbnailOnly flag
183
+ */
184
+ image = GetThumbnail ( path , ThumbnailOptions . ThumbnailOnly ) ;
166
185
}
167
186
}
168
187
else
169
188
{
170
- image = ImageCache [ Constant . ErrorIcon ] ;
171
- path = Constant . ErrorIcon ;
172
- }
173
-
174
- if ( type != ImageType . Error )
175
- {
176
- image . Freeze ( ) ;
189
+ type = ImageType . File ;
190
+ image = GetThumbnail ( path , ThumbnailOptions . None ) ;
177
191
}
178
192
}
179
- catch ( System . Exception e )
193
+ else
194
+ {
195
+ image = _imageCache [ Constant . ErrorIcon ] ;
196
+ path = Constant . ErrorIcon ;
197
+ }
198
+
199
+ if ( type != ImageType . Error )
180
200
{
181
- Log . Exception ( $ "|ImageLoader.Load|Failed to get thumbnail for { path } ", e ) ;
182
- type = ImageType . Error ;
183
- image = ImageCache [ Constant . ErrorIcon ] ;
184
- ImageCache [ path ] = image ;
201
+ image . Freeze ( ) ;
185
202
}
203
+
186
204
return new ImageResult ( image , type ) ;
187
205
}
188
206
189
- private static bool EnableImageHash = true ;
207
+ private static BitmapSource GetThumbnail ( string path , ThumbnailOptions option = ThumbnailOptions . ThumbnailOnly )
208
+ {
209
+ return WindowsThumbnailProvider . GetThumbnail (
210
+ path ,
211
+ Constant . ThumbnailSize ,
212
+ Constant . ThumbnailSize ,
213
+ option ) ;
214
+ }
190
215
191
216
public static ImageSource Load ( string path , bool loadFullImage = false )
192
217
{
193
218
var imageResult = LoadInternal ( path , loadFullImage ) ;
194
219
195
220
var img = imageResult . ImageSource ;
196
221
if ( imageResult . ImageType != ImageType . Error && imageResult . ImageType != ImageType . Cache )
197
- { // we need to get image hash
198
- string hash = EnableImageHash ? _hashGenerator . GetHashFromImage ( img ) : null ;
222
+ {
223
+ // we need to get image hash
224
+ string hash = _enableHashImage ? _hashGenerator . GetHashFromImage ( img ) : null ;
199
225
if ( hash != null )
200
226
{
201
- if ( GuidToKey . TryGetValue ( hash , out string key ) )
202
- { // image already exists
203
- img = ImageCache [ key ] ;
227
+ if ( _guidToKey . TryGetValue ( hash , out string key ) )
228
+ {
229
+ // image already exists
230
+ img = _imageCache [ key ] ;
204
231
}
205
232
else
206
- { // new guid
207
- GuidToKey [ hash ] = path ;
233
+ {
234
+ // new guid
235
+ _guidToKey [ hash ] = path ;
208
236
}
209
237
}
210
238
211
239
// update cache
212
- ImageCache [ path ] = img ;
240
+ _imageCache [ path ] = img ;
213
241
}
214
242
215
-
216
243
return img ;
217
244
}
218
245
0 commit comments