@@ -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 )
0 commit comments