22using System . Collections . Concurrent ;
33using System . IO ;
44using System . Linq ;
5+ using System . Threading ;
56using System . Threading . Tasks ;
67using System . Windows . Media ;
78using System . Windows . Media . Imaging ;
@@ -14,6 +15,8 @@ public static class ImageLoader
1415 {
1516 private static readonly ImageCache ImageCache = new ImageCache ( ) ;
1617 private static BinaryStorage < ConcurrentDictionary < string , int > > _storage ;
18+ private static readonly ConcurrentDictionary < string , string > GuidToKey = new ConcurrentDictionary < string , string > ( ) ;
19+ private static IImageHashGenerator _hashGenerator ;
1720
1821
1922 private static readonly string [ ] ImageExtensions =
@@ -30,7 +33,8 @@ public static class ImageLoader
3033
3134 public static void Initialize ( )
3235 {
33- _storage = new BinaryStorage < ConcurrentDictionary < string , int > > ( "Image" ) ;
36+ _storage = new BinaryStorage < ConcurrentDictionary < string , int > > ( "Image" ) ;
37+ _hashGenerator = new ImageHashGenerator ( ) ;
3438 ImageCache . Usage = _storage . TryLoad ( new ConcurrentDictionary < string , int > ( ) ) ;
3539
3640 foreach ( var icon in new [ ] { Constant . DefaultIcon , Constant . ErrorIcon } )
@@ -43,16 +47,12 @@ public static void Initialize()
4347 {
4448 Stopwatch . Normal ( "|ImageLoader.Initialize|Preload images cost" , ( ) =>
4549 {
46- ImageCache . Usage . AsParallel ( ) . Where ( i => ! ImageCache . ContainsKey ( i . Key ) ) . ForAll ( i =>
50+ ImageCache . Usage . AsParallel ( ) . Where ( i => ! ImageCache . ContainsKey ( i . Key ) ) . ForAll ( x =>
4751 {
48- var img = Load ( i . Key ) ;
49- if ( img != null )
50- {
51- ImageCache [ i . Key ] = img ;
52- }
52+ Load ( x . Key ) ;
5353 } ) ;
5454 } ) ;
55- Log . Info ( $ "|ImageLoader.Initialize|Number of preload images is <{ ImageCache . Usage . Count } >") ;
55+ Log . Info ( $ "|ImageLoader.Initialize|Number of preload images is <{ ImageCache . Usage . Count } >, Images Number: { ImageCache . CacheSize ( ) } , Unique Items { ImageCache . UniqueImagesInCache ( ) } ") ;
5656 } ) ;
5757 }
5858
@@ -61,31 +61,54 @@ public static void Save()
6161 ImageCache . Cleanup ( ) ;
6262 _storage . Save ( ImageCache . Usage ) ;
6363 }
64-
65- public static ImageSource Load ( string path , bool loadFullImage = false )
64+
65+ private class ImageResult
66+ {
67+ public ImageResult ( ImageSource imageSource , ImageType imageType )
68+ {
69+ ImageSource = imageSource ;
70+ ImageType = imageType ;
71+ }
72+
73+ public ImageType ImageType { get ; }
74+ public ImageSource ImageSource { get ; }
75+ }
76+
77+ private enum ImageType
78+ {
79+ File ,
80+ Folder ,
81+ Data ,
82+ ImageFile ,
83+ Error ,
84+ Cache
85+ }
86+
87+ private static ImageResult LoadInternal ( string path , bool loadFullImage = false )
6688 {
6789 ImageSource image ;
90+ ImageType type = ImageType . Error ;
6891 try
6992 {
7093 if ( string . IsNullOrEmpty ( path ) )
7194 {
72- return ImageCache [ Constant . ErrorIcon ] ;
95+ return new ImageResult ( ImageCache [ Constant . ErrorIcon ] , ImageType . Error ) ;
7396 }
7497 if ( ImageCache . ContainsKey ( path ) )
7598 {
76- return ImageCache [ path ] ;
99+ return new ImageResult ( ImageCache [ path ] , ImageType . Cache ) ;
77100 }
78-
101+
79102 if ( path . StartsWith ( "data:" , StringComparison . OrdinalIgnoreCase ) )
80103 {
81- return new BitmapImage ( new Uri ( path ) ) ;
104+ return new ImageResult ( new BitmapImage ( new Uri ( path ) ) , ImageType . Data ) ;
82105 }
83106
84107 if ( ! Path . IsPathRooted ( path ) )
85108 {
86109 path = Path . Combine ( Constant . ProgramDirectory , "Images" , Path . GetFileName ( path ) ) ;
87110 }
88-
111+
89112 if ( Directory . Exists ( path ) )
90113 {
91114 /* Directories can also have thumbnails instead of shell icons.
@@ -94,14 +117,17 @@ public static ImageSource Load(string path, bool loadFullImage = false)
94117 * Wox responsibility.
95118 * - Solution: just load the icon
96119 */
120+ type = ImageType . Folder ;
97121 image = WindowsThumbnailProvider . GetThumbnail ( path , Constant . ThumbnailSize ,
98122 Constant . ThumbnailSize , ThumbnailOptions . IconOnly ) ;
123+
99124 }
100125 else if ( File . Exists ( path ) )
101126 {
102127 var extension = Path . GetExtension ( path ) . ToLower ( ) ;
103128 if ( ImageExtensions . Contains ( extension ) )
104129 {
130+ type = ImageType . ImageFile ;
105131 if ( loadFullImage )
106132 {
107133 image = LoadFullImage ( path ) ;
@@ -119,6 +145,7 @@ public static ImageSource Load(string path, bool loadFullImage = false)
119145 }
120146 else
121147 {
148+ type = ImageType . File ;
122149 image = WindowsThumbnailProvider . GetThumbnail ( path , Constant . ThumbnailSize ,
123150 Constant . ThumbnailSize , ThumbnailOptions . None ) ;
124151 }
@@ -128,17 +155,51 @@ public static ImageSource Load(string path, bool loadFullImage = false)
128155 image = ImageCache [ Constant . ErrorIcon ] ;
129156 path = Constant . ErrorIcon ;
130157 }
131- ImageCache [ path ] = image ;
132- image . Freeze ( ) ;
158+
159+ if ( type != ImageType . Error )
160+ {
161+ image . Freeze ( ) ;
162+ }
133163 }
134164 catch ( System . Exception e )
135165 {
136166 Log . Exception ( $ "|ImageLoader.Load|Failed to get thumbnail for { path } ", e ) ;
137-
167+ type = ImageType . Error ;
138168 image = ImageCache [ Constant . ErrorIcon ] ;
139169 ImageCache [ path ] = image ;
140170 }
141- return image ;
171+ return new ImageResult ( image , type ) ;
172+ }
173+
174+ private static bool EnableImageHash = true ;
175+
176+ public static ImageSource Load ( string path , bool loadFullImage = false )
177+ {
178+ // return LoadInternal(path, loadFullImage).ImageSource;
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