2014-07-14 19:03:52 +08:00
|
|
|
|
using System;
|
2016-04-22 06:37:40 +08:00
|
|
|
|
using System.Collections.Concurrent;
|
2014-07-14 19:03:52 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Windows;
|
2016-01-07 05:34:42 +08:00
|
|
|
|
using System.Windows.Interop;
|
2014-07-14 19:03:52 +08:00
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
using System.Windows.Media.Imaging;
|
2016-04-22 06:37:40 +08:00
|
|
|
|
using System.Linq;
|
2014-12-18 19:22:47 +08:00
|
|
|
|
using Wox.Infrastructure;
|
2016-04-21 08:53:21 +08:00
|
|
|
|
using Wox.Infrastructure.Storage;
|
2014-07-14 19:03:52 +08:00
|
|
|
|
|
2014-12-18 19:22:47 +08:00
|
|
|
|
namespace Wox.ImageLoader
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
|
|
|
|
public class ImageLoader
|
|
|
|
|
{
|
2016-04-23 05:42:26 +08:00
|
|
|
|
private readonly ConcurrentDictionary<string, ImageSource> _imageSources = new ConcurrentDictionary<string, ImageSource>();
|
2014-07-14 19:03:52 +08:00
|
|
|
|
|
2016-04-22 06:37:40 +08:00
|
|
|
|
private readonly List<string> ImageExts = new List<string>
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
|
|
|
|
".png",
|
|
|
|
|
".jpg",
|
|
|
|
|
".jpeg",
|
|
|
|
|
".gif",
|
|
|
|
|
".bmp",
|
|
|
|
|
".tiff",
|
|
|
|
|
".ico"
|
|
|
|
|
};
|
|
|
|
|
|
2016-04-22 06:37:40 +08:00
|
|
|
|
private readonly List<string> SelfExts = new List<string>
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
|
|
|
|
".exe",
|
|
|
|
|
".lnk",
|
|
|
|
|
".ani",
|
|
|
|
|
".cur",
|
|
|
|
|
".sln",
|
|
|
|
|
".appref-ms"
|
|
|
|
|
};
|
|
|
|
|
|
2016-04-22 06:37:40 +08:00
|
|
|
|
private readonly ImageCache _cache;
|
2016-04-26 06:20:16 +08:00
|
|
|
|
private readonly BinaryStorage<ImageCache> _storage;
|
2016-03-28 10:09:57 +08:00
|
|
|
|
|
2016-04-22 06:37:40 +08:00
|
|
|
|
public ImageLoader()
|
2016-03-28 10:09:57 +08:00
|
|
|
|
{
|
2016-04-21 08:53:21 +08:00
|
|
|
|
_storage = new BinaryStorage<ImageCache>();
|
|
|
|
|
_cache = _storage.Load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ImageLoader()
|
|
|
|
|
{
|
|
|
|
|
_storage.Save();
|
2016-03-28 10:09:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-22 06:37:40 +08:00
|
|
|
|
private ImageSource GetIcon(string fileName)
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
2015-01-15 20:47:48 +08:00
|
|
|
|
try
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
2015-01-15 20:47:48 +08:00
|
|
|
|
if (File.Exists(fileName) == false) return null;
|
|
|
|
|
Icon icon = GetFileIcon(fileName) ?? Icon.ExtractAssociatedIcon(fileName);
|
|
|
|
|
if (icon != null)
|
|
|
|
|
{
|
2016-01-07 05:34:42 +08:00
|
|
|
|
return Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
|
2015-01-15 20:47:48 +08:00
|
|
|
|
new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
|
|
|
|
|
}
|
2014-07-14 19:03:52 +08:00
|
|
|
|
}
|
2015-11-05 05:35:04 +08:00
|
|
|
|
catch { }
|
2014-07-14 19:03:52 +08:00
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-22 06:37:40 +08:00
|
|
|
|
public void PreloadImages()
|
2014-12-18 19:22:47 +08:00
|
|
|
|
{
|
2016-04-22 06:37:40 +08:00
|
|
|
|
Stopwatch.Debug($"Preload {_cache.TopUsedImages.Count} images", () =>
|
2014-12-18 19:22:47 +08:00
|
|
|
|
{
|
2016-04-23 05:42:26 +08:00
|
|
|
|
_cache.TopUsedImages.AsParallel().Where(i => !_imageSources.ContainsKey(i.Key)).ForAll(i =>
|
2016-04-22 06:37:40 +08:00
|
|
|
|
{
|
|
|
|
|
var img = Load(i.Key, false);
|
|
|
|
|
if (img != null)
|
|
|
|
|
{
|
|
|
|
|
// todo happlebao magic
|
|
|
|
|
// the image created on other threads can be accessed from main ui thread,
|
|
|
|
|
// this line made it possible
|
|
|
|
|
// should be changed the Dispatcher.InvokeAsync in the future
|
|
|
|
|
img.Freeze();
|
2016-04-23 05:42:26 +08:00
|
|
|
|
_imageSources[i.Key] = img;
|
2016-04-22 06:37:40 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
2015-11-05 05:35:04 +08:00
|
|
|
|
});
|
2014-12-18 19:22:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-22 06:37:40 +08:00
|
|
|
|
public ImageSource Load(string path, bool addToCache = true)
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
2015-11-05 05:35:04 +08:00
|
|
|
|
if (string.IsNullOrEmpty(path)) return null;
|
2016-04-26 06:20:16 +08:00
|
|
|
|
ImageSource image = null;
|
2015-11-05 05:49:36 +08:00
|
|
|
|
Stopwatch.Debug($"Loading image path: {path}", () =>
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
2015-11-05 05:35:04 +08:00
|
|
|
|
|
2015-11-02 08:04:05 +08:00
|
|
|
|
if (addToCache)
|
2015-02-04 23:16:41 +08:00
|
|
|
|
{
|
2016-04-21 08:53:21 +08:00
|
|
|
|
_cache.Add(path);
|
2015-02-04 23:16:41 +08:00
|
|
|
|
}
|
2015-11-02 08:04:05 +08:00
|
|
|
|
|
2015-11-05 05:35:04 +08:00
|
|
|
|
|
2016-04-23 05:42:26 +08:00
|
|
|
|
if (_imageSources.ContainsKey(path))
|
2015-02-04 23:16:41 +08:00
|
|
|
|
{
|
2016-04-26 06:20:16 +08:00
|
|
|
|
image = _imageSources[path];
|
2015-02-04 23:16:41 +08:00
|
|
|
|
}
|
2015-11-02 08:04:05 +08:00
|
|
|
|
else
|
2015-02-04 23:16:41 +08:00
|
|
|
|
{
|
2015-11-02 08:04:05 +08:00
|
|
|
|
string ext = Path.GetExtension(path).ToLower();
|
|
|
|
|
|
|
|
|
|
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
2016-04-26 06:20:16 +08:00
|
|
|
|
image = new BitmapImage(new Uri(path));
|
2015-11-02 08:04:05 +08:00
|
|
|
|
}
|
2016-04-22 06:37:40 +08:00
|
|
|
|
else if (SelfExts.Contains(ext) && File.Exists(path))
|
2015-11-02 08:04:05 +08:00
|
|
|
|
{
|
2016-04-26 06:20:16 +08:00
|
|
|
|
image = GetIcon(path);
|
2015-11-02 08:04:05 +08:00
|
|
|
|
}
|
2016-04-26 06:20:16 +08:00
|
|
|
|
else if (!string.IsNullOrEmpty(path) && ImageExts.Contains(ext))
|
2015-11-02 08:04:05 +08:00
|
|
|
|
{
|
2016-04-26 06:20:16 +08:00
|
|
|
|
if (File.Exists(path))
|
|
|
|
|
{
|
|
|
|
|
image = new BitmapImage(new Uri(path));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
path = Path.Combine(WoxDirectroy.Executable, "Images", Path.GetFileName(path));
|
|
|
|
|
if (File.Exists(path))
|
|
|
|
|
{
|
|
|
|
|
image = new BitmapImage(new Uri(path));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
path = Path.Combine(WoxDirectroy.Executable, "Images", "app.png");
|
|
|
|
|
image = new BitmapImage(new Uri(path));
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-02 08:04:05 +08:00
|
|
|
|
}
|
2014-07-16 20:17:51 +08:00
|
|
|
|
|
2016-04-26 06:20:16 +08:00
|
|
|
|
if (image != null && addToCache)
|
2015-02-04 23:16:41 +08:00
|
|
|
|
{
|
2016-04-23 05:42:26 +08:00
|
|
|
|
if (!_imageSources.ContainsKey(path))
|
2015-11-02 08:04:05 +08:00
|
|
|
|
{
|
2016-04-26 06:20:16 +08:00
|
|
|
|
_imageSources[path] = image;
|
2015-11-02 08:04:05 +08:00
|
|
|
|
}
|
2015-02-04 23:16:41 +08:00
|
|
|
|
}
|
2014-07-14 19:03:52 +08:00
|
|
|
|
}
|
2015-11-05 05:35:04 +08:00
|
|
|
|
});
|
2016-04-26 06:20:16 +08:00
|
|
|
|
return image;
|
2014-07-14 19:03:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// http://blogs.msdn.com/b/oldnewthing/archive/2011/01/27/10120844.aspx
|
2016-04-22 06:37:40 +08:00
|
|
|
|
private Icon GetFileIcon(string name)
|
2014-07-14 19:03:52 +08:00
|
|
|
|
{
|
|
|
|
|
SHFILEINFO shfi = new SHFILEINFO();
|
|
|
|
|
uint flags = SHGFI_SYSICONINDEX;
|
|
|
|
|
|
|
|
|
|
IntPtr himl = SHGetFileInfo(name,
|
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
|
ref shfi,
|
|
|
|
|
(uint)Marshal.SizeOf(shfi),
|
|
|
|
|
flags);
|
|
|
|
|
|
|
|
|
|
if (himl != IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
IntPtr hIcon = ImageList_GetIcon(himl, shfi.iIcon, ILD_NORMAL);
|
|
|
|
|
var icon = (Icon)Icon.FromHandle(hIcon).Clone();
|
|
|
|
|
DestroyIcon(hIcon);
|
|
|
|
|
return icon;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[DllImport("comctl32.dll", SetLastError = true)]
|
|
|
|
|
private static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, uint flags);
|
|
|
|
|
|
|
|
|
|
private const int MAX_PATH = 256;
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
private struct SHITEMID
|
|
|
|
|
{
|
|
|
|
|
public ushort cb;
|
|
|
|
|
[MarshalAs(UnmanagedType.LPArray)]
|
|
|
|
|
public byte[] abID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
private struct ITEMIDLIST
|
|
|
|
|
{
|
|
|
|
|
public SHITEMID mkid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
private struct BROWSEINFO
|
|
|
|
|
{
|
|
|
|
|
public IntPtr hwndOwner;
|
|
|
|
|
public IntPtr pidlRoot;
|
|
|
|
|
public IntPtr pszDisplayName;
|
|
|
|
|
[MarshalAs(UnmanagedType.LPTStr)]
|
|
|
|
|
public string lpszTitle;
|
|
|
|
|
public uint ulFlags;
|
|
|
|
|
public IntPtr lpfn;
|
|
|
|
|
public int lParam;
|
|
|
|
|
public IntPtr iImage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Browsing for directory.
|
|
|
|
|
private const uint BIF_RETURNONLYFSDIRS = 0x0001;
|
|
|
|
|
private const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
|
|
|
|
|
private const uint BIF_STATUSTEXT = 0x0004;
|
|
|
|
|
private const uint BIF_RETURNFSANCESTORS = 0x0008;
|
|
|
|
|
private const uint BIF_EDITBOX = 0x0010;
|
|
|
|
|
private const uint BIF_VALIDATE = 0x0020;
|
|
|
|
|
private const uint BIF_NEWDIALOGSTYLE = 0x0040;
|
|
|
|
|
private const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
|
|
|
|
|
private const uint BIF_BROWSEINCLUDEURLS = 0x0080;
|
|
|
|
|
private const uint BIF_BROWSEFORCOMPUTER = 0x1000;
|
|
|
|
|
private const uint BIF_BROWSEFORPRINTER = 0x2000;
|
|
|
|
|
private const uint BIF_BROWSEINCLUDEFILES = 0x4000;
|
|
|
|
|
private const uint BIF_SHAREABLE = 0x8000;
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
private struct SHFILEINFO
|
|
|
|
|
{
|
|
|
|
|
public const int NAMESIZE = 80;
|
|
|
|
|
public IntPtr hIcon;
|
|
|
|
|
public int iIcon;
|
|
|
|
|
public uint dwAttributes;
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
|
|
|
|
|
public string szDisplayName;
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
|
|
|
|
|
public string szTypeName;
|
2016-01-07 05:34:42 +08:00
|
|
|
|
}
|
2014-07-14 19:03:52 +08:00
|
|
|
|
|
|
|
|
|
private const uint SHGFI_ICON = 0x000000100; // get icon
|
|
|
|
|
private const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
|
|
|
|
|
private const uint SHGFI_TYPENAME = 0x000000400; // get type name
|
|
|
|
|
private const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
|
|
|
|
|
private const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
|
|
|
|
|
private const uint SHGFI_EXETYPE = 0x000002000; // return exe type
|
|
|
|
|
private const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
|
|
|
|
|
private const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
|
|
|
|
|
private const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
|
|
|
|
|
private const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
|
|
|
|
|
private const uint SHGFI_LARGEICON = 0x000000000; // get large icon
|
|
|
|
|
private const uint SHGFI_SMALLICON = 0x000000001; // get small icon
|
|
|
|
|
private const uint SHGFI_OPENICON = 0x000000002; // get open icon
|
|
|
|
|
private const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
|
|
|
|
|
private const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
|
|
|
|
|
private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
|
|
|
|
|
private const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
|
|
|
|
|
private const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
|
|
|
|
|
|
|
|
|
|
private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
|
|
|
|
|
private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
|
|
|
|
|
private const uint ILD_NORMAL = 0x00000000;
|
|
|
|
|
|
|
|
|
|
[DllImport("Shell32.dll")]
|
|
|
|
|
private static extern IntPtr SHGetFileInfo(
|
|
|
|
|
string pszPath,
|
|
|
|
|
uint dwFileAttributes,
|
|
|
|
|
ref SHFILEINFO psfi,
|
|
|
|
|
uint cbFileInfo,
|
|
|
|
|
uint uFlags
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
[DllImport("User32.dll")]
|
|
|
|
|
private static extern int DestroyIcon(IntPtr hIcon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|