测试环境:
测试图片(30M):
测试计时方法:
Stopwatch sw1 = new Stopwatch(); sw1.Start(); //TODO...... sw1.Stop(); string xx = sw1.ElapsedMilliseconds.ToString(); MessageBox.Show(xx);
方法一,(1张30M图片,用时799毫秒)
public Image getThumbNailUsingGetThumbnailImage(string fileName) { Image img = Image.FromFile(fileName); return img.GetThumbnailImage(300, 300, null, IntPtr.Zero); }
方法二,(1张30M图片,用时1329毫秒)
public Image createThumbnailUsingGDI(ref Image imgPhoto, int destWidth, int destHeight) { int sourceX = 0; int sourceY = 0; int destX = 0; int destY = 0; int sourceWidth = imgPhoto.Width; int sourceHeight = imgPhoto.Height; Bitmap b = new Bitmap(destWidth, destHeight); Graphics grPhoto = Graphics.FromImage(b); grPhoto.FillRectangle(Brushes.DarkGray, new Rectangle(destX, destY, destWidth, destHeight)); grPhoto.DrawLine(new Pen(Brushes.LightGray), new Point(0, destHeight - 1), new Point(destWidth, destHeight - 1)); grPhoto.DrawLine(new Pen(Brushes.LightGray), new Point(destWidth - 1, 0), new Point(destWidth - 1, destHeight)); //shade right grPhoto.FillRectangle(Brushes.White, new Rectangle(destWidth - 3, 0, 7, 2)); grPhoto.FillRectangle(Brushes.White, new Rectangle(destWidth - 2, 0, 7, 4)); grPhoto.FillRectangle(Brushes.White, new Rectangle(destWidth - 1, 0, 7, 6)); //shade botton grPhoto.FillRectangle(Brushes.White, new Rectangle(0, destHeight - 3, 2, 7)); grPhoto.FillRectangle(Brushes.White, new Rectangle(0, destHeight - 2, 4, 7)); grPhoto.FillRectangle(Brushes.White, new Rectangle(0, destHeight - 1, 6, 7)); grPhoto.DrawImage(imgPhoto, new Rectangle(destX + 2, destY + 2, destWidth - 7, destHeight - 7), new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), GraphicsUnit.Pixel); grPhoto.Dispose(); return b; }
方法三,(1张30M图片,用时1636毫秒)
public Image getThumbNailWithFrame(string fileName) { FileStream fs = new FileStream(fileName, FileMode.Open); Image im = Image.FromStream(fs); Size szMax = new Size(300, 300); Size sz = getProportionalSize(szMax, im.Size); // superior image quality Bitmap bmpResized = new Bitmap(sz.Width, sz.Height); using (Graphics g = Graphics.FromImage(bmpResized)) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.FillRectangle(Brushes.White, 0, 0, sz.Width, sz.Height); int FrameWidth = 5;//decides the frame border width g.DrawRectangle(new Pen(Color.Silver, FrameWidth - 2), 0, 0, sz.Width - 1, sz.Height - 1); FrameWidth += 5;//decide the frame width g.DrawImage(im, new Rectangle(FrameWidth, FrameWidth, sz.Width - FrameWidth * 2, sz.Height - FrameWidth * 2), new Rectangle(Point.Empty, im.Size), GraphicsUnit.Pixel); } im.Dispose(); im = null; fs.Close(); fs.Dispose(); fs = null; return bmpResized; } private Size getProportionalSize(Size szMax, Size szReal) { int nWidth; int nHeight; double sMaxRatio; double sRealRatio; if (szMax.Width < 1 || szMax.Height < 1 || szReal.Width < 1 || szReal.Height < 1) return Size.Empty; sMaxRatio = (double)szMax.Width / (double)szMax.Height; sRealRatio = (double)szReal.Width / (double)szReal.Height; if (sMaxRatio < sRealRatio) { nWidth = Math.Min(szMax.Width, szReal.Width); nHeight = (int)Math.Round(nWidth / sRealRatio); } else { nHeight = Math.Min(szMax.Height, szReal.Height); nWidth = (int)Math.Round(nHeight * sRealRatio); } return new Size(nWidth, nHeight); }
方法四,(1张30M图片,用时1664毫秒)
public Image getThumbNail(string fileName) { FileStream fs = new FileStream(fileName, FileMode.Open); Image im = Image.FromStream(fs); Size szMax = new Size(300, 300); Size sz = getProportionalSize(szMax, im.Size); // superior image quality Bitmap bmpResized = new Bitmap(sz.Width, sz.Height); using (Graphics g = Graphics.FromImage(bmpResized)) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage( im, new Rectangle(Point.Empty, sz), new Rectangle(Point.Empty, im.Size), GraphicsUnit.Pixel); } im.Dispose(); im = null; fs.Close(); fs.Dispose(); fs = null; return bmpResized; } private Size getProportionalSize(Size szMax, Size szReal) { int nWidth; int nHeight; double sMaxRatio; double sRealRatio; if (szMax.Width < 1 || szMax.Height < 1 || szReal.Width < 1 || szReal.Height < 1) return Size.Empty; sMaxRatio = (double)szMax.Width / (double)szMax.Height; sRealRatio = (double)szReal.Width / (double)szReal.Height; if (sMaxRatio < sRealRatio) { nWidth = Math.Min(szMax.Width, szReal.Width); nHeight = (int)Math.Round(nWidth / sRealRatio); } else { nHeight = Math.Min(szMax.Height, szReal.Height); nWidth = (int)Math.Round(nHeight * sRealRatio); } return new Size(nWidth, nHeight); }
方法五,(1张30M图片,用时735毫秒)
public Image createThumbFromProperty(string file) { Image image = new Bitmap(file); Image Thumb = null; PropertyItem[] propItems = image.PropertyItems; foreach (PropertyItem propItem in propItems) { if (propItem.Id == 0x501B) { byte[] imageBytes = propItem.Value; MemoryStream stream = new MemoryStream(imageBytes.Length); stream.Write(imageBytes, 0, imageBytes.Length); Thumb = Image.FromStream(stream); break; } } return Thumb; }
方法六,(50张30M图片,237毫秒)
using System; using System.Diagnostics; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text; //网址:https://www.experts-exchange.com/questions/21789724/How-to-create-thumbnail.html namespace 缩略图查看 { public class ShellThumbnail : IDisposable { [Flags] private enum ESTRRET { STRRET_WSTR = 0, STRRET_OFFSET = 1, STRRET_CSTR = 2 } [Flags] private enum ESHCONTF { SHCONTF_FOLDERS = 32, SHCONTF_NONFOLDERS = 64, SHCONTF_INCLUDEHIDDEN = 128, } [Flags] private enum ESHGDN { SHGDN_NORMAL = 0, SHGDN_INFOLDER = 1, SHGDN_FORADDRESSBAR = 16384, SHGDN_FORPARSING = 32768 } [Flags] private enum ESFGAO { SFGAO_CANCOPY = 1, SFGAO_CANMOVE = 2, SFGAO_CANLINK = 4, SFGAO_CANRENAME = 16, SFGAO_CANDELETE = 32, SFGAO_HASPROPSHEET = 64, SFGAO_DROPTARGET = 256, SFGAO_CAPABILITYMASK = 375, SFGAO_LINK = 65536, SFGAO_SHARE = 131072, SFGAO_READONLY = 262144, SFGAO_GHOSTED = 524288, SFGAO_DISPLAYATTRMASK = 983040, SFGAO_FILESYSANCESTOR = 268435456, SFGAO_FOLDER = 536870912, SFGAO_FILESYSTEM = 1073741824, SFGAO_HASSUBFOLDER = -2147483648, SFGAO_CONTENTSMASK = -2147483648, SFGAO_VALIDATE = 16777216, SFGAO_REMOVABLE = 33554432, SFGAO_COMPRESSED = 67108864, } private enum EIEIFLAG { IEIFLAG_ASYNC = 1, IEIFLAG_CACHE = 2, IEIFLAG_ASPECT = 4, IEIFLAG_OFFLINE = 8, IEIFLAG_GLEAM = 16, IEIFLAG_SCREEN = 32, IEIFLAG_ORIGSIZE = 64, IEIFLAG_NOSTAMP = 128, IEIFLAG_NOBORDER = 256, IEIFLAG_QUALITY = 512 } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0, CharSet = CharSet.Auto)] private struct STRRET_CSTR { public ESTRRET uType; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 520)] public byte[] cStr; } [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)] private struct STRRET_ANY { [FieldOffset(0)] public ESTRRET uType; [FieldOffset(4)] public IntPtr pOLEString; } [StructLayoutAttribute(LayoutKind.Sequential)] private struct SIZE { public int cx; public int cy; } [ComImport(), Guid("00000000-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IUnknown { [PreserveSig()] IntPtr QueryInterface(ref Guid riid, ref IntPtr pVoid); [PreserveSig()] IntPtr AddRef(); [PreserveSig()] IntPtr Release(); } [ComImportAttribute()] [GuidAttribute("00000002-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IMalloc { [PreserveSig()] IntPtr Alloc(int cb); [PreserveSig()] IntPtr Realloc(IntPtr pv, int cb); [PreserveSig()] void Free(IntPtr pv); [PreserveSig()] int GetSize(IntPtr pv); [PreserveSig()] int DidAlloc(IntPtr pv); [PreserveSig()] void HeapMinimize(); } [ComImportAttribute()] [GuidAttribute("000214F2-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IEnumIDList { [PreserveSig()] int Next(int celt, ref IntPtr rgelt, ref int pceltFetched); void Skip(int celt); void Reset(); void Clone(ref IEnumIDList ppenum); } [ComImportAttribute()] [GuidAttribute("000214E6-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellFolder { void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved, [MarshalAs(UnmanagedType.LPWStr)]string lpszDisplayName, ref int pchEaten, ref IntPtr ppidl, ref int pdwAttributes); void EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)]ESHCONTF grfFlags, ref IEnumIDList ppenumIDList); void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, ref IShellFolder ppvOut); void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj); [PreserveSig()] int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2); void CreateViewObject(IntPtr hwndOwner, ref Guid riid, IntPtr ppvOut); void GetAttributesOf(int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.U4)]ref ESFGAO rgfInOut); void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, ref int prgfInOut, ref IUnknown ppvOut); void GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)]ESHGDN uFlags, ref STRRET_CSTR lpName); void SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)]string lpszName, [MarshalAs(UnmanagedType.U4)] ESHCONTF uFlags, ref IntPtr ppidlOut); } [ComImportAttribute(), GuidAttribute("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IExtractImage { void GetLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref SIZE prgSize, int dwRecClrDepth, ref int pdwFlags); void Extract(ref IntPtr phBmpThumbnail); } private class UnmanagedMethods { [DllImport("shell32", CharSet = CharSet.Auto)] internal extern static int SHGetMalloc(ref IMalloc ppMalloc); [DllImport("shell32", CharSet = CharSet.Auto)] internal extern static int SHGetDesktopFolder(ref IShellFolder ppshf); [DllImport("shell32", CharSet = CharSet.Auto)] internal extern static int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath); [DllImport("gdi32", CharSet = CharSet.Auto)] internal extern static int DeleteObject(IntPtr hObject); } ~ShellThumbnail() { Dispose(); } private IMalloc alloc = null; private bool disposed = false; private Size _desiredSize = new Size(100, 100); private Bitmap _thumbNail; public Bitmap ThumbNail { get { return _thumbNail; } } public Size DesiredSize { get { return _desiredSize; } set { _desiredSize = value; } } private IMalloc Allocator { get { if (!disposed) { if (alloc == null) { UnmanagedMethods.SHGetMalloc(ref alloc); } } else { Debug.Assert(false, "Object has been disposed."); } return alloc; } } public Bitmap GetThumbnail(string fileName) { if (string.IsNullOrEmpty(fileName)) return null; if (!File.Exists(fileName) && !Directory.Exists(fileName)) { throw new FileNotFoundException(string.Format("The file '{0}' does not exist", fileName), fileName); } if (_thumbNail != null) { _thumbNail.Dispose(); _thumbNail = null; } IShellFolder folder = null; try { folder = getDesktopFolder; } catch (Exception ex) { throw ex; } if (folder != null) { IntPtr pidlMain = IntPtr.Zero; try { int cParsed = 0; int pdwAttrib = 0; string filePath = Path.GetDirectoryName(fileName); folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, ref cParsed, ref pidlMain, ref pdwAttrib); } catch (Exception ex) { Marshal.ReleaseComObject(folder); throw ex; } if (pidlMain != IntPtr.Zero) { Guid iidShellFolder = new Guid("000214E6-0000-0000-C000-000000000046"); IShellFolder item = null; try { folder.BindToObject(pidlMain, IntPtr.Zero, ref iidShellFolder, ref item); } catch (Exception ex) { Marshal.ReleaseComObject(folder); Allocator.Free(pidlMain); throw ex; } if (item != null) { IEnumIDList idEnum = null; try { item.EnumObjects(IntPtr.Zero, (ESHCONTF.SHCONTF_FOLDERS | ESHCONTF.SHCONTF_NONFOLDERS), ref idEnum); } catch (Exception ex) { Marshal.ReleaseComObject(folder); Allocator.Free(pidlMain); throw ex; } if (idEnum != null) { int hRes = 0; IntPtr pidl = IntPtr.Zero; int fetched = 0; bool complete = false; while (!complete) { hRes = idEnum.Next(1, ref pidl, ref fetched); if (hRes != 0) { pidl = IntPtr.Zero; complete = true; } else { if (_getThumbNail(fileName, pidl, item)) { complete = true; } } if (pidl != IntPtr.Zero) { Allocator.Free(pidl); } } Marshal.ReleaseComObject(idEnum); } Marshal.ReleaseComObject(item); } Allocator.Free(pidlMain); } Marshal.ReleaseComObject(folder); } return ThumbNail; } private bool _getThumbNail(string file, IntPtr pidl, IShellFolder item) { IntPtr hBmp = IntPtr.Zero; IExtractImage extractImage = null; try { string pidlPath = PathFromPidl(pidl); if (Path.GetFileName(pidlPath).ToUpper().Equals(Path.GetFileName(file).ToUpper())) { IUnknown iunk = null; int prgf = 0; Guid iidExtractImage = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1"); item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, ref prgf, ref iunk); extractImage = (IExtractImage)iunk; if (extractImage != null) { Console.WriteLine("Got an IExtractImage object!"); SIZE sz = new SIZE(); sz.cx = DesiredSize.Width; sz.cy = DesiredSize.Height; StringBuilder location = new StringBuilder(260, 260); int priority = 0; int requestedColourDepth = 32; EIEIFLAG flags = EIEIFLAG.IEIFLAG_ASPECT | EIEIFLAG.IEIFLAG_SCREEN; int uFlags = (int)flags; try { extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags); extractImage.Extract(ref hBmp); } catch (System.Runtime.InteropServices.COMException ex) { } if (hBmp != IntPtr.Zero) { _thumbNail = Bitmap.FromHbitmap(hBmp); } Marshal.ReleaseComObject(extractImage); extractImage = null; } return true; } else { return false; } } catch (Exception ex) { if (hBmp != IntPtr.Zero) { UnmanagedMethods.DeleteObject(hBmp); } if (extractImage != null) { Marshal.ReleaseComObject(extractImage); } throw ex; } } private string PathFromPidl(IntPtr pidl) { StringBuilder path = new StringBuilder(260, 260); int result = UnmanagedMethods.SHGetPathFromIDList(pidl, path); if (result == 0) { return string.Empty; } else { return path.ToString(); } } private IShellFolder getDesktopFolder { get { IShellFolder ppshf = null; int r = UnmanagedMethods.SHGetDesktopFolder(ref ppshf); return ppshf; } } public void Dispose() { if (!disposed) { if (alloc != null) { Marshal.ReleaseComObject(alloc); } alloc = null; if (_thumbNail != null) { _thumbNail.Dispose(); } disposed = true; } } } }
方法七,(50张30M图片,96毫秒)
速度最快,只要兄弟们推荐的多,我下午公布源码。^_^
最快速度源码,请接收
using System; using System.IO; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Drawing.Drawing2D; namespace 缩略图查看 { /// <summary> /// Allows reading of embedded thumbnail image from the EXIF information in an image. /// </summary> //public abstract class ExifThumbReader public class ExifThumbReader { // GDI plus functions [DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipGetPropertyItem(IntPtr image, int propid, int size, IntPtr buffer); [DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipGetPropertyItemSize(IntPtr image, int propid, out int size); [DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] internal static extern int GdipLoadImageFromFile(string filename, out IntPtr image); [DllImport("gdiplus.dll", EntryPoint = "GdipDisposeImage", CharSet = CharSet.Unicode, ExactSpelling = true)] private static extern int GdipDisposeImage(IntPtr image); // EXIT tag value for thumbnail data. Value specified by EXIF standard private static int THUMBNAIL_DATA = 0x501B; /// <summary> /// Reads the thumbnail in the given image. If no thumbnail is found, returns null /// </summary> //public static Image ReadThumb(string imagePath) public Image ReadThumb(string imagePath) { const int GDI_ERR_PROP_NOT_FOUND = 19; // Property not found error const int GDI_ERR_OUT_OF_MEMORY = 3; IntPtr hImage = IntPtr.Zero; IntPtr buffer = IntPtr.Zero; // Holds the thumbnail data int ret; ret = GdipLoadImageFromFile(imagePath, out hImage); try { if (ret != 0) throw createException(ret); int propSize; ret = GdipGetPropertyItemSize(hImage, THUMBNAIL_DATA, out propSize); // Image has no thumbnail data in it. Return null if (ret == GDI_ERR_PROP_NOT_FOUND) return null; if (ret != 0) throw createException(ret); // Allocate a buffer in memory buffer = Marshal.AllocHGlobal(propSize); if (buffer == IntPtr.Zero) throw createException(GDI_ERR_OUT_OF_MEMORY); ret = GdipGetPropertyItem(hImage, THUMBNAIL_DATA, propSize, buffer); if (ret != 0) throw createException(ret); // buffer has the thumbnail data. Now we have to convert it to // an Image return convertFromMemory(buffer); } finally { // Free the buffer if (buffer != IntPtr.Zero) Marshal.FreeHGlobal(buffer); GdipDisposeImage(hImage); } } /// <summary> /// Generates an exception depending on the GDI+ error codes (I removed some error /// codes) /// </summary> private static Exception createException(int gdipErrorCode) { switch (gdipErrorCode) { case 1: return new ExternalException("Gdiplus Generic Error", -2147467259); case 2: return new ArgumentException("Gdiplus Invalid Parameter"); case 3: return new OutOfMemoryException("Gdiplus Out Of Memory"); case 4: return new InvalidOperationException("Gdiplus Object Busy"); case 5: return new OutOfMemoryException("Gdiplus Insufficient Buffer"); case 7: return new ExternalException("Gdiplus Generic Error", -2147467259); case 8: return new InvalidOperationException("Gdiplus Wrong State"); case 9: return new ExternalException("Gdiplus Aborted", -2147467260); case 10: return new FileNotFoundException("Gdiplus File Not Found"); case 11: return new OverflowException("Gdiplus Over flow"); case 12: return new ExternalException("Gdiplus Access Denied", -2147024891); case 13: return new ArgumentException("Gdiplus Unknown Image Format"); case 18: return new ExternalException("Gdiplus Not Initialized", -2147467259); case 20: return new ArgumentException("Gdiplus Property Not Supported Error"); } return new ExternalException("Gdiplus Unknown Error", -2147418113); } /// <summary> /// Converts the IntPtr buffer to a property item and then converts its /// value to a Drawing.Image item /// </summary> private static Image convertFromMemory(IntPtr thumbData) { propertyItemInternal prop = (propertyItemInternal)Marshal.PtrToStructure (thumbData, typeof(propertyItemInternal)); // The image data is in the form of a byte array. Write all // the bytes to a stream and create a new image from that stream byte[] imageBytes = prop.Value; MemoryStream stream = new MemoryStream(imageBytes.Length); stream.Write(imageBytes, 0, imageBytes.Length); return Image.FromStream(stream); } /// <summary> /// Used in Marshal.PtrToStructure(). /// We need this dummy class because Imaging.PropertyItem is not a "blittable" /// class and Marshal.PtrToStructure only accepted blittable classes. /// (It's not blitable because it uses a byte[] array and that's not a blittable /// type. See MSDN for a definition of Blittable.) /// </summary> [StructLayout(LayoutKind.Sequential)] private class propertyItemInternal { public int id = 0; public int len = 0; public short type = 0; public IntPtr value = IntPtr.Zero; public byte[] Value { get { byte[] bytes = new byte[(uint)len]; Marshal.Copy(value, bytes, 0, len); return bytes; } } } } }
测试结果:
方法 | 图片张数(张) | 每张大小(M) | 图片总大小 | 用时 |
方法一 | 50 | 30 | 1500M | 39950毫秒=40秒 |
方法二 | 50 | 30 | 1500M | 66450毫秒=66秒 |
方法三 | 50 | 30 | 1500M | 81800毫秒=81秒 |
方法四 | 50 | 30 | 1500M | 83200毫秒=83秒 |
方法五 | 50 | 30 | 1500M | 36750毫秒=37秒 |
方法六 | 50 | 30 | 1500M | 237毫秒 |
方法七 | 50 | 30 | 1500M | 96毫秒 |