PowerToys/Wox.Infrastructure/Image/ImageLoader.cs
bao-qian dc3b01dc15 use %APPDATA%
1. Fix can't find Result.ctor bug for plugin introduced in
c0889de1f9ae460b2cc189eb59e5bd90ddb7d17e
2. use %APPDATA% for all data, part of #389
3. MISC
2016-04-27 02:35:17 +01:00

280 lines
9.9 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Wox.Infrastructure.Storage;
namespace Wox.Infrastructure.Image
{
public static class ImageLoader
{
private static readonly ConcurrentDictionary<string, ImageSource> _imageSources = new ConcurrentDictionary<string, ImageSource>();
private static readonly List<string> ImageExts = new List<string>
{
".png",
".jpg",
".jpeg",
".gif",
".bmp",
".tiff",
".ico"
};
private static readonly List<string> SelfExts = new List<string>
{
".exe",
".lnk",
".ani",
".cur",
".sln",
".appref-ms"
};
private static readonly ImageCache _cache;
private static readonly BinaryStorage<ImageCache> _storage;
static ImageLoader()
{
_storage = new BinaryStorage<ImageCache>();
_cache = _storage.Load();
}
public static void Save()
{
_storage.Save();
}
private static ImageSource GetIcon(string fileName)
{
try
{
if (File.Exists(fileName) == false) return null;
Icon icon = GetFileIcon(fileName) ?? Icon.ExtractAssociatedIcon(fileName);
if (icon != null)
{
return Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
}
}
catch { }
return null;
}
public static void PreloadImages()
{
Stopwatch.Debug($"Preload {_cache.TopUsedImages.Count} images", () =>
{
_cache.TopUsedImages.AsParallel().Where(i => !_imageSources.ContainsKey(i.Key)).ForAll(i =>
{
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();
_imageSources[i.Key] = img;
}
});
});
}
public static ImageSource Load(string path, bool addToCache = true)
{
ImageSource image = null;
if (string.IsNullOrEmpty(path))
{
path = Path.Combine(Wox.ProgramPath, "Images", "app.png");
image = new BitmapImage(new Uri(path));
return image;
}
Stopwatch.Debug($"Loading image path: {path}", () =>
{
if (addToCache)
{
_cache.Add(path);
}
if (_imageSources.ContainsKey(path))
{
image = _imageSources[path];
}
else
{
string ext = Path.GetExtension(path).ToLower();
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
image = new BitmapImage(new Uri(path));
}
else if (SelfExts.Contains(ext) && File.Exists(path))
{
image = GetIcon(path);
}
else if (!string.IsNullOrEmpty(path) && ImageExts.Contains(ext))
{
if (File.Exists(path))
{
image = new BitmapImage(new Uri(path));
}
else
{
path = Path.Combine(Wox.ProgramPath, "Images", Path.GetFileName(path));
if (File.Exists(path))
{
image = new BitmapImage(new Uri(path));
}
else
{
path = Path.Combine(Wox.ProgramPath, "Images", "app.png");
image = new BitmapImage(new Uri(path));
}
}
}
if (image != null && addToCache)
{
if (!_imageSources.ContainsKey(path))
{
_imageSources[path] = image;
}
}
}
});
return image;
}
// http://blogs.msdn.com/b/oldnewthing/archive/2011/01/27/10120844.aspx
private static Icon GetFileIcon(string name)
{
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;
}
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);
}
}