initial
This commit is contained in:
		
							
								
								
									
										219
									
								
								Definitions/Reference.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								Definitions/Reference.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | ||||
| using System; | ||||
| using System.Drawing; | ||||
| using System.IO; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Runtime.InteropServices.ComTypes; | ||||
|  | ||||
| namespace PhotoThumbnail.Definitions | ||||
| { | ||||
|     internal static class HandlerNativeMethods | ||||
|     { | ||||
|         internal static readonly Guid IThumbnailProviderGuid = new Guid("e357fccd-a995-4576-b01f-234630154e96"); | ||||
|  | ||||
|         //internal static readonly Guid IInitializeWithFileGuid = new Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"); | ||||
|         internal static readonly Guid IInitializeWithStreamGuid = new Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"); | ||||
|         //internal static readonly Guid IInitializeWithItemGuid = new Guid("7f73be3f-fb79-493c-a6c7-7ee14e245841"); | ||||
|  | ||||
|         internal static readonly Guid IMarshalGuid = new Guid("00000003-0000-0000-C000-000000000046"); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// This interface exposes the <see cref="ConsructBitmap"/> function for initializing the  | ||||
|     /// Thumbnail Provider with a <typeparamref name="Stream"/>. | ||||
|     /// If this interfaces is not used, then the handler must opt out of process isolation. | ||||
|     /// This interface can be used in conjunction with the other intialization interfaces, | ||||
|     /// but only 1 will be accessed according to the priorities preset by the Windows Shell: | ||||
|     /// <typeparamref name="IThumbnailFromStream"/> | ||||
|     /// <typeparamref name="IThumbnailFromShellObject"/> | ||||
|     /// <typeparamref name="IThumbnailFromFile"/> | ||||
|     /// </summary> | ||||
|     public interface IThumbnailFromStream | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Provides the <typeparamref name="Stream"/> to the item from which a thumbnail should be created. | ||||
|         /// <remarks>Only 32bpp bitmaps support adornments.  | ||||
|         /// While 24bpp bitmaps will be displayed they will not display adornments. | ||||
|         /// Additional guidelines for developing thumbnails can be found at http://msdn.microsoft.com/en-us/library/cc144115(v=VS.85).aspx | ||||
|         /// </remarks> | ||||
|         /// </summary> | ||||
|         /// <param name="stream">Stream to initialize the thumbnail</param> | ||||
|         /// <param name="sideSize">Square side dimension in which the thumbnail should fit; the thumbnail will be scaled otherwise.</param> | ||||
|         /// <returns></returns> | ||||
|         Bitmap ConstructBitmap(Stream stream, int sideSize); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// The STGM constants are flags that indicate  | ||||
|     /// conditions for creating and deleting the object and access modes  | ||||
|     /// for the object.  | ||||
|     ///  | ||||
|     /// You can combine these flags, but you can only choose one flag  | ||||
|     /// from each group of related flags. Typically one flag from each  | ||||
|     /// of the access and sharing groups must be specified for all  | ||||
|     /// functions and methods which use these constants.  | ||||
|     /// </summary> | ||||
|     [Flags] | ||||
|     public enum AccessModes | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Indicates that, in direct mode, each change to a storage  | ||||
|         /// or stream element is written as it occurs. | ||||
|         /// </summary> | ||||
|         Direct = 0x00000000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Indicates that, in transacted mode, changes are buffered  | ||||
|         /// and written only if an explicit commit operation is called.  | ||||
|         /// </summary> | ||||
|         Transacted = 0x00010000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Provides a faster implementation of a compound file  | ||||
|         /// in a limited, but frequently used, case.  | ||||
|         /// </summary> | ||||
|         Simple = 0x08000000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Indicates that the object is read-only,  | ||||
|         /// meaning that modifications cannot be made. | ||||
|         /// </summary> | ||||
|         Read = 0x00000000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Enables you to save changes to the object,  | ||||
|         /// but does not permit access to its data.  | ||||
|         /// </summary> | ||||
|         Write = 0x00000001, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Enables access and modification of object data. | ||||
|         /// </summary> | ||||
|         ReadWrite = 0x00000002, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Specifies that subsequent openings of the object are  | ||||
|         /// not denied read or write access.  | ||||
|         /// </summary> | ||||
|         ShareDenyNone = 0x00000040, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Prevents others from subsequently opening the object in Read mode.  | ||||
|         /// </summary> | ||||
|         ShareDenyRead = 0x00000030, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Prevents others from subsequently opening the object  | ||||
|         /// for Write or ReadWrite access. | ||||
|         /// </summary> | ||||
|         ShareDenyWrite = 0x00000020, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Prevents others from subsequently opening the object in any mode.  | ||||
|         /// </summary> | ||||
|         ShareExclusive = 0x00000010, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Opens the storage object with exclusive access to the most  | ||||
|         /// recently committed version. | ||||
|         /// </summary> | ||||
|         Priority = 0x00040000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Indicates that the underlying file is to be automatically destroyed when the root  | ||||
|         /// storage object is released. This feature is most useful for creating temporary files.  | ||||
|         /// </summary> | ||||
|         DeleteOnRelease = 0x04000000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Indicates that, in transacted mode, a temporary scratch file is usually used  | ||||
|         /// to save modifications until the Commit method is called.  | ||||
|         /// Specifying NoScratch permits the unused portion of the original file  | ||||
|         /// to be used as work space instead of creating a new file for that purpose.  | ||||
|         /// </summary> | ||||
|         NoScratch = 0x00100000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Indicates that an existing storage object  | ||||
|         /// or stream should be removed before the new object replaces it.  | ||||
|         /// </summary> | ||||
|         Create = 0x00001000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates the new object while preserving existing data in a stream named "Contents".  | ||||
|         /// </summary> | ||||
|         Convert = 0x00020000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Causes the create operation to fail if an existing object with the specified name exists. | ||||
|         /// </summary> | ||||
|         FailIfThere = 0x00000000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// This flag is used when opening a storage object with Transacted  | ||||
|         /// and without ShareExclusive or ShareDenyWrite.  | ||||
|         /// In this case, specifying NoSnapshot prevents the system-provided  | ||||
|         /// implementation from creating a snapshot copy of the file.  | ||||
|         /// Instead, changes to the file are written to the end of the file.  | ||||
|         /// </summary> | ||||
|         NoSnapshot = 0x00200000, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Supports direct mode for single-writer, multireader file operations.  | ||||
|         /// </summary> | ||||
|         DirectSingleWriterMultipleReader = 0x00400000 | ||||
|     } | ||||
|  | ||||
|     // <summary> | ||||
|     /// Thumbnail Alpha Types | ||||
|     /// </summary> | ||||
|     public enum ThumbnailAlphaType | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Let the system decide. | ||||
|         /// </summary> | ||||
|         Unknown = 0, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// No transparency | ||||
|         /// </summary> | ||||
|         NoAlphaChannel = 1, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Has transparency | ||||
|         /// </summary> | ||||
|         HasAlphaChannel = 2, | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ComVisible interface for native IThumbnailProvider | ||||
|     /// </summary> | ||||
|     [ComImport] | ||||
|     [Guid("e357fccd-a995-4576-b01f-234630154e96")] | ||||
|     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | ||||
|     interface IThumbnailProvider | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Gets a pointer to a bitmap to display as a thumbnail | ||||
|         /// </summary> | ||||
|         /// <param name="squareLength"></param> | ||||
|         /// <param name="bitmapHandle"></param> | ||||
|         /// <param name="bitmapType"></param> | ||||
|         void GetThumbnail(uint squareLength, [Out] out IntPtr bitmapHandle, [Out] out uint bitmapType); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Provides means by which to initialize with a stream. | ||||
|     /// </summary> | ||||
|     [ComImport] | ||||
|     [Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f")] | ||||
|     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] | ||||
|     interface IInitializeWithStream | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Initializes with a stream. | ||||
|         /// </summary> | ||||
|         /// <param name="stream"></param> | ||||
|         /// <param name="fileMode"></param> | ||||
|         void Initialize(IStream stream, AccessModes fileMode); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										265
									
								
								Definitions/StorageStream.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								Definitions/StorageStream.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,265 @@ | ||||
| using System; | ||||
| using System.IO; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Runtime.InteropServices.ComTypes; | ||||
|  | ||||
| namespace PhotoThumbnail.Definitions | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A wrapper for the native IStream object. | ||||
|     /// </summary> | ||||
|     public class StorageStream : Stream, IDisposable | ||||
|     { | ||||
|         private IStream _stream; | ||||
|         private readonly bool _isReadOnly; | ||||
|  | ||||
|         internal StorageStream(IStream stream, bool readOnly) | ||||
|         { | ||||
|             _stream = stream ?? throw new ArgumentNullException("stream"); | ||||
|             _isReadOnly = readOnly; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Reads a single byte from the stream, moving the current position ahead by 1. | ||||
|         /// </summary> | ||||
|         /// <returns>A single byte from the stream, -1 if end of stream.</returns> | ||||
|         public override int ReadByte() | ||||
|         { | ||||
|             ThrowIfDisposed(); | ||||
|  | ||||
|             byte[] buffer = new byte[1]; | ||||
|             if (Read(buffer, 0, 1) > 0) | ||||
|             { | ||||
|                 return buffer[0]; | ||||
|             } | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Writes a single byte to the stream | ||||
|         /// </summary> | ||||
|         /// <param name="value">Byte to write to stream</param> | ||||
|         public override void WriteByte(byte value) | ||||
|         { | ||||
|             ThrowIfDisposed(); | ||||
|             byte[] buffer = new byte[] { value }; | ||||
|             Write(buffer, 0, 1); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets whether the stream can be read from. | ||||
|         /// </summary> | ||||
|         public override bool CanRead => _stream != null; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets whether seeking is supported by the stream. | ||||
|         /// </summary> | ||||
|         public override bool CanSeek => _stream != null; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets whether the stream can be written to. | ||||
|         /// Always false. | ||||
|         /// </summary> | ||||
|         public override bool CanWrite => _stream != null && !_isReadOnly; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Reads a buffer worth of bytes from the stream. | ||||
|         /// </summary> | ||||
|         /// <param name="buffer">Buffer to fill</param> | ||||
|         /// <param name="offset">Offset to start filling in the buffer</param> | ||||
|         /// <param name="count">Number of bytes to read from the stream</param> | ||||
|         /// <returns></returns> | ||||
|         public override int Read(byte[] buffer, int offset, int count) | ||||
|         { | ||||
|             ThrowIfDisposed(); | ||||
|  | ||||
|             if (buffer == null) { throw new ArgumentNullException("buffer"); } | ||||
|             if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "StorageStreamOffsetLessThanZero"); } | ||||
|             if (count < 0) { throw new ArgumentOutOfRangeException("count", "StorageStreamCountLessThanZero"); } | ||||
|             if (offset + count > buffer.Length) { throw new ArgumentException("StorageStreamBufferOverflow", "count"); } | ||||
|  | ||||
|             int bytesRead = 0; | ||||
|             if (count > 0) | ||||
|             { | ||||
|                 IntPtr ptr = Marshal.AllocCoTaskMem(sizeof(ulong)); | ||||
|                 try | ||||
|                 { | ||||
|                     if (offset == 0) | ||||
|                     { | ||||
|                         _stream.Read(buffer, count, ptr); | ||||
|                         bytesRead = (int)Marshal.ReadInt64(ptr); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         byte[] tempBuffer = new byte[count]; | ||||
|                         _stream.Read(tempBuffer, count, ptr); | ||||
|  | ||||
|                         bytesRead = (int)Marshal.ReadInt64(ptr); | ||||
|                         if (bytesRead > 0) | ||||
|                         { | ||||
|                             Array.Copy(tempBuffer, 0, buffer, offset, bytesRead); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 finally | ||||
|                 { | ||||
|                     Marshal.FreeCoTaskMem(ptr); | ||||
|                 } | ||||
|             } | ||||
|             return bytesRead; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Writes a buffer to the stream if able to do so. | ||||
|         /// </summary> | ||||
|         /// <param name="buffer">Buffer to write</param> | ||||
|         /// <param name="offset">Offset in buffer to start writing</param> | ||||
|         /// <param name="count">Number of bytes to write to the stream</param> | ||||
|         public override void Write(byte[] buffer, int offset, int count) | ||||
|         { | ||||
|             ThrowIfDisposed(); | ||||
|  | ||||
|             if (_isReadOnly) { throw new InvalidOperationException("StorageStreamIsReadonly"); } | ||||
|             if (buffer == null) { throw new ArgumentNullException("buffer"); } | ||||
|             if (offset < 0) { throw new ArgumentOutOfRangeException("offset", "StorageStreamOffsetLessThanZero"); } | ||||
|             if (count < 0) { throw new ArgumentOutOfRangeException("count", "StorageStreamCountLessThanZero"); } | ||||
|             if (offset + count > buffer.Length) { throw new ArgumentException("StorageStreamBufferOverflow", "count"); } | ||||
|  | ||||
|             if (count > 0) | ||||
|             { | ||||
|                 IntPtr ptr = Marshal.AllocCoTaskMem(sizeof(ulong)); | ||||
|                 try | ||||
|                 { | ||||
|                     if (offset == 0) | ||||
|                     { | ||||
|                         _stream.Write(buffer, count, ptr); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         byte[] tempBuffer = new byte[count]; | ||||
|                         Array.Copy(buffer, offset, tempBuffer, 0, count); | ||||
|                         _stream.Write(tempBuffer, count, ptr); | ||||
|                     } | ||||
|                 } | ||||
|                 finally | ||||
|                 { | ||||
|                     Marshal.FreeCoTaskMem(ptr); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the length of the IStream | ||||
|         /// </summary> | ||||
|         public override long Length | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 ThrowIfDisposed(); | ||||
|                 const int STATFLAG_NONAME = 1; | ||||
|                 _stream.Stat(out System.Runtime.InteropServices.ComTypes.STATSTG stats, STATFLAG_NONAME); | ||||
|                 return stats.cbSize; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets or sets the current position within the underlying IStream. | ||||
|         /// </summary> | ||||
|         public override long Position | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 ThrowIfDisposed(); | ||||
|                 return Seek(0, SeekOrigin.Current); | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 ThrowIfDisposed(); | ||||
|                 Seek(value, SeekOrigin.Begin); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Seeks within the underlying IStream. | ||||
|         /// </summary> | ||||
|         /// <param name="offset">Offset</param> | ||||
|         /// <param name="origin">Where to start seeking</param> | ||||
|         /// <returns></returns> | ||||
|         public override long Seek(long offset, SeekOrigin origin) | ||||
|         { | ||||
|             ThrowIfDisposed(); | ||||
|             IntPtr ptr = Marshal.AllocCoTaskMem(sizeof(long)); | ||||
|             try | ||||
|             { | ||||
|                 _stream.Seek(offset, (int)origin, ptr); | ||||
|                 return Marshal.ReadInt64(ptr); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 Marshal.FreeCoTaskMem(ptr); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets the length of the stream | ||||
|         /// </summary> | ||||
|         /// <param name="value"></param> | ||||
|         public override void SetLength(long value) | ||||
|         { | ||||
|             ThrowIfDisposed(); | ||||
|             _stream.SetSize(value); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Commits data to be written to the stream if it is being cached. | ||||
|         /// </summary> | ||||
|         public override void Flush() | ||||
|         { | ||||
|             _stream.Commit((int)StorageStreamCommitOptions.None); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Disposes the stream. | ||||
|         /// </summary> | ||||
|         /// <param name="disposing">True if called from Dispose(), false if called from finalizer.</param> | ||||
|         protected override void Dispose(bool disposing) | ||||
|         { | ||||
|             _stream = null; | ||||
|             base.Dispose(disposing); | ||||
|         } | ||||
|  | ||||
|         private void ThrowIfDisposed() { if (_stream == null) throw new ObjectDisposedException(GetType().Name); } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Options for commiting (flushing) an IStream storage stream | ||||
|     /// </summary> | ||||
|     [Flags] | ||||
|     internal enum StorageStreamCommitOptions | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Uses default options | ||||
|         /// </summary> | ||||
|         None = 0, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Overwrite option | ||||
|         /// </summary> | ||||
|         Overwrite = 1, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Only if current | ||||
|         /// </summary> | ||||
|         OnlyIfCurrent = 2, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Commits to disk cache dangerously | ||||
|         /// </summary> | ||||
|         DangerouslyCommitMerelyToDiskCache = 4, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Consolidate | ||||
|         /// </summary> | ||||
|         Consolidate = 8 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										256
									
								
								Definitions/ThumbnailProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								Definitions/ThumbnailProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | ||||
| using Microsoft.Win32; | ||||
| using System; | ||||
| using System.Drawing; | ||||
| using System.Linq; | ||||
| using System.Runtime.InteropServices; | ||||
|  | ||||
| namespace PhotoThumbnail.Definitions | ||||
| { | ||||
|     public abstract class ThumbnailProvider : IThumbnailProvider, ICustomQueryInterface, IDisposable, IInitializeWithStream | ||||
|     { | ||||
|         private Bitmap GetBitmap(int sideLength) | ||||
|         { | ||||
|             if (_stream != null && this is IThumbnailFromStream stream) | ||||
|             { | ||||
|                 return stream.ConstructBitmap(_stream, sideLength); | ||||
|             } | ||||
|  | ||||
|             throw new InvalidOperationException("ThumbnailProviderInterfaceNotImplemented: " + GetType().Name); | ||||
|         } | ||||
|  | ||||
|         public virtual ThumbnailAlphaType ThumbnailAlphaType => ThumbnailAlphaType.Unknown; | ||||
|  | ||||
|         private StorageStream _stream; | ||||
|  | ||||
|         #region IThumbnailProvider Members | ||||
|  | ||||
|         void IThumbnailProvider.GetThumbnail(uint sideLength, out IntPtr hBitmap, out uint alphaType) | ||||
|         { | ||||
|             using (Bitmap map = GetBitmap((int)sideLength)) | ||||
|             { | ||||
|                 hBitmap = map.GetHbitmap(); | ||||
|             } | ||||
|             alphaType = (uint)ThumbnailAlphaType; | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|  | ||||
|         #region ICustomQueryInterface Members | ||||
|  | ||||
|         CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) | ||||
|         { | ||||
|             ppv = IntPtr.Zero; | ||||
|  | ||||
|             // Forces COM to not use the managed (free threaded) marshaler | ||||
|             if (iid == HandlerNativeMethods.IMarshalGuid) | ||||
|             { | ||||
|                 return CustomQueryInterfaceResult.Failed; | ||||
|             } | ||||
|  | ||||
|             if (iid == HandlerNativeMethods.IInitializeWithStreamGuid && !(this is IThumbnailFromStream)) | ||||
|             { | ||||
|                 return CustomQueryInterfaceResult.Failed; | ||||
|             } | ||||
|  | ||||
|             return CustomQueryInterfaceResult.NotHandled; | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|  | ||||
|         #region COM Registration | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Called when the assembly is registered via RegAsm. | ||||
|         /// </summary> | ||||
|         /// <param name="registerType">Type to be registered.</param> | ||||
|         [ComRegisterFunction] | ||||
|         private static void Register(Type registerType) | ||||
|         { | ||||
|             if (registerType != null && registerType.IsSubclassOf(typeof(ThumbnailProvider))) | ||||
|             { | ||||
|                 object[] attributes = registerType.GetCustomAttributes(typeof(ThumbnailProviderAttribute), true); | ||||
|                 if (attributes != null && attributes.Length == 1) | ||||
|                 { | ||||
|                     ThumbnailProviderAttribute attribute = attributes[0] as ThumbnailProviderAttribute; | ||||
|                     ThrowIfInvalid(registerType, attribute); | ||||
|                     RegisterThumbnailHandler(registerType.GUID.ToString("B"), attribute); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private static void RegisterThumbnailHandler(string guid, ThumbnailProviderAttribute attribute) | ||||
|         { | ||||
|             // set process isolation | ||||
|             using (RegistryKey clsidKey = Registry.ClassesRoot.OpenSubKey("CLSID")) | ||||
|             using (RegistryKey guidKey = clsidKey.OpenSubKey(guid, true)) | ||||
|             { | ||||
|                 guidKey.SetValue("DisableProcessIsolation", attribute.DisableProcessIsolation ? 1 : 0, RegistryValueKind.DWord); | ||||
|  | ||||
|                 using (RegistryKey inproc = guidKey.OpenSubKey("InprocServer32", true)) | ||||
|                 { | ||||
|                     inproc.SetValue("ThreadingModel", "Apartment", RegistryValueKind.String); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // register file as an approved extension | ||||
|             using (RegistryKey approvedShellExtensions = Registry.LocalMachine.OpenSubKey( | ||||
|                  @"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true)) | ||||
|             { | ||||
|                 approvedShellExtensions.SetValue(guid, attribute.Name, RegistryValueKind.String); | ||||
|             } | ||||
|  | ||||
|             // register extension with each extension in the list | ||||
|             string[] extensions = attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); | ||||
|             foreach (string extension in extensions) | ||||
|             { | ||||
|                 using (RegistryKey extensionKey = Registry.ClassesRoot.CreateSubKey(extension)) // Create makes it writable | ||||
|                 using (RegistryKey shellExKey = extensionKey.CreateSubKey("shellex")) | ||||
|                 using (RegistryKey providerKey = shellExKey.CreateSubKey(HandlerNativeMethods.IThumbnailProviderGuid.ToString("B"))) | ||||
|                 { | ||||
|                     providerKey.SetValue(null, guid, RegistryValueKind.String); | ||||
|  | ||||
|                     if (attribute.ThumbnailCutoff == ThumbnailCutoffSize.Square20) | ||||
|                     { | ||||
|                         extensionKey.DeleteValue("ThumbnailCutoff", false); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         extensionKey.SetValue("ThumbnailCutoff", (int)attribute.ThumbnailCutoff, RegistryValueKind.DWord); | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                     if (attribute.TypeOverlay != null) | ||||
|                     { | ||||
|                         extensionKey.SetValue("TypeOverlay", attribute.TypeOverlay, RegistryValueKind.String); | ||||
|                     } | ||||
|  | ||||
|                     if (attribute.ThumbnailAdornment == ThumbnailAdornment.Default) | ||||
|                     { | ||||
|                         extensionKey.DeleteValue("Treatment", false); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         extensionKey.SetValue("Treatment", (int)attribute.ThumbnailAdornment, RegistryValueKind.DWord); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Called when the assembly is registered via RegAsm. | ||||
|         /// </summary> | ||||
|         /// <param name="registerType">Type to register.</param> | ||||
|         [ComUnregisterFunction] | ||||
|         private static void Unregister(Type registerType) | ||||
|         { | ||||
|             if (registerType != null && registerType.IsSubclassOf(typeof(ThumbnailProvider))) | ||||
|             { | ||||
|                 object[] attributes = registerType.GetCustomAttributes(typeof(ThumbnailProviderAttribute), true); | ||||
|                 if (attributes != null && attributes.Length == 1) | ||||
|                 { | ||||
|                     ThumbnailProviderAttribute attribute = attributes[0] as ThumbnailProviderAttribute; | ||||
|                     UnregisterThumbnailHandler(registerType.GUID.ToString("B"), attribute); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private static void UnregisterThumbnailHandler(string guid, ThumbnailProviderAttribute attribute) | ||||
|         { | ||||
|             string[] extensions = attribute.Extensions.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); | ||||
|             foreach (string extension in extensions) | ||||
|             { | ||||
|                 using (RegistryKey extKey = Registry.ClassesRoot.OpenSubKey(extension, true)) | ||||
|                 using (RegistryKey shellexKey = extKey.OpenSubKey("shellex", true)) | ||||
|                 { | ||||
|                     shellexKey.DeleteSubKey(HandlerNativeMethods.IThumbnailProviderGuid.ToString("B"), false); | ||||
|  | ||||
|                     extKey.DeleteValue("ThumbnailCutoff", false); | ||||
|                     extKey.DeleteValue("TypeOverlay", false); | ||||
|                     extKey.DeleteValue("Treatment", false); // Thumbnail adornment | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             using (RegistryKey approvedShellExtensions = Registry.LocalMachine.OpenSubKey( | ||||
|                 @"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved", true)) | ||||
|             { | ||||
|                 approvedShellExtensions.DeleteValue(guid, false); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private static void ThrowIfInvalid(Type type, ThumbnailProviderAttribute attribute) | ||||
|         { | ||||
|             if (attribute is null) | ||||
|             { | ||||
|                 throw new ArgumentNullException(nameof(attribute)); | ||||
|             } | ||||
|  | ||||
|             var interfaces = type.GetInterfaces(); | ||||
|             bool interfaced = interfaces.Any(x => x == typeof(IThumbnailFromStream)); | ||||
|  | ||||
|             /* | ||||
|             if (interfaces.Any(x => x == typeof(IThumbnailFromShellObject) || x == typeof(IThumbnailFromFile))) | ||||
|             { | ||||
|                 // According to MSDN (http://msdn.microsoft.com/en-us/library/cc144114(v=VS.85).aspx) | ||||
|                 // A thumbnail provider that does not implement IInitializeWithStream must opt out of  | ||||
|                 // running in the isolated process. The default behavior of the indexer opts in | ||||
|                 // to process isolation regardless of which interfaces are implemented.                 | ||||
|                 if (!interfaced && !attribute.DisableProcessIsolation) | ||||
|                 { | ||||
|                     throw new InvalidOperationException("ThumbnailProviderDisabledProcessIsolation: " + type.Name); | ||||
|                 } | ||||
|                 interfaced = true; | ||||
|             } | ||||
|             */ | ||||
|  | ||||
|             if (!interfaced) | ||||
|             { | ||||
|                 throw new InvalidOperationException("ThumbnailProviderInterfaceNotImplemented: " + type.Name); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|  | ||||
|         #region IInitializeWithStream Members | ||||
|  | ||||
|         void IInitializeWithStream.Initialize(System.Runtime.InteropServices.ComTypes.IStream stream, AccessModes fileMode) | ||||
|         { | ||||
|             _stream = new StorageStream(stream, fileMode != AccessModes.ReadWrite); | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|  | ||||
|         #region IDisposable Members | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Finalizer for the thumbnail provider. | ||||
|         /// </summary> | ||||
|         ~ThumbnailProvider() | ||||
|         { | ||||
|             Dispose(false); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Disposes the thumbnail provider. | ||||
|         /// </summary> | ||||
|         public void Dispose() | ||||
|         { | ||||
|             Dispose(true); | ||||
|             GC.SuppressFinalize(this); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Disploses the thumbnail provider. | ||||
|         /// </summary> | ||||
|         /// <param name="disposing"></param> | ||||
|         protected virtual void Dispose(bool disposing) | ||||
|         { | ||||
|             if (disposing && _stream != null) | ||||
|             { | ||||
|                 _stream.Dispose(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|     } | ||||
| } | ||||
							
								
								
									
										136
									
								
								Definitions/ThumbnailProviderAttribute.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Definitions/ThumbnailProviderAttribute.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| using System; | ||||
|  | ||||
| namespace PhotoThumbnail.Definitions | ||||
| { | ||||
|     /// <summary>     | ||||
|     /// This class attribute is applied to a Thumbnail Provider to specify registration parameters | ||||
|     /// and aesthetic attributes. | ||||
|     /// </summary> | ||||
|     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] | ||||
|     public sealed class ThumbnailProviderAttribute : Attribute | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Creates a new instance of the attribute. | ||||
|         /// </summary> | ||||
|         /// <param name="name">Name of the provider</param> | ||||
|         /// <param name="extensions">Semi-colon-separated list of extensions supported by this provider.</param> | ||||
|         public ThumbnailProviderAttribute(string name, string extensions) | ||||
|         { | ||||
|             Name = name ?? throw new ArgumentNullException("name"); | ||||
|             Extensions = extensions ?? throw new ArgumentNullException("extensions"); | ||||
|  | ||||
|             DisableProcessIsolation = false; | ||||
|             ThumbnailCutoff = ThumbnailCutoffSize.Square20; | ||||
|             TypeOverlay = null; | ||||
|             ThumbnailAdornment = ThumbnailAdornment.Default; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the name of the provider | ||||
|         /// </summary> | ||||
|         public string Name { get; private set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the semi-colon-separated list of extensions supported by the provider. | ||||
|         /// </summary> | ||||
|         public string Extensions { get; private set; } | ||||
|  | ||||
|         // optional parameters below. | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Opts-out of running within the surrogate process DllHost.exe. | ||||
|         /// This will reduce robustness and security. | ||||
|         /// This value should be true if the provider does not implement <typeparamref name="IThumbnailFromStream"/>. | ||||
|         /// </summary> | ||||
|         // Note: The msdn documentation and property name are contradicting. | ||||
|         // http://msdn.microsoft.com/en-us/library/cc144118(VS.85).aspx | ||||
|         public bool DisableProcessIsolation { get; set; } // If true: Makes it run IN PROCESS. | ||||
|  | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Below this size thumbnail images will not be generated - file icons will be used instead. | ||||
|         /// </summary> | ||||
|         public ThumbnailCutoffSize ThumbnailCutoff { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// A resource reference string pointing to the icon to be used as an overlay on the bottom right of the thumbnail. | ||||
|         /// ex. ISVComponent.dll@,-155 | ||||
|         /// ex. C:\Windows\System32\SampleIcon.ico | ||||
|         /// If an empty string is provided, no overlay will be used. | ||||
|         /// If the property is set to null, the default icon for the associated icon will be used as an overlay. | ||||
|         /// </summary> | ||||
|         public string TypeOverlay { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Specifies the <typeparamref name="ThumbnailAdornment"/> for the thumbnail. | ||||
|         /// <remarks> | ||||
|         /// Only 32bpp bitmaps support adornments.  | ||||
|         /// While 24bpp bitmaps will be displayed, their adornments will not. | ||||
|         /// If an adornment is specified by the file-type's associated application,  | ||||
|         /// the applications adornment will override the value specified in this registration.</remarks> | ||||
|         /// </summary> | ||||
|         public ThumbnailAdornment ThumbnailAdornment { get; set; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Defines the minimum thumbnail size for which thumbnails will be generated. | ||||
|     /// </summary> | ||||
|     public enum ThumbnailCutoffSize | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Default size of 20x20 | ||||
|         /// </summary> | ||||
|         Square20 = -1, //For 20x20, you do not add any key in the registry | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Size of 32x32 | ||||
|         /// </summary> | ||||
|         Square32 = 0, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Size of 16x16 | ||||
|         /// </summary> | ||||
|         Square16 = 1, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Size of 48x48 | ||||
|         /// </summary> | ||||
|         Square48 = 2, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Size of 16x16. An alternative to Square16. | ||||
|         /// </summary> | ||||
|         Square16B = 3 | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Adornment applied to thumbnails. | ||||
|     /// </summary> | ||||
|     public enum ThumbnailAdornment | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// This will use the associated application's default icon as the adornment. | ||||
|         /// </summary> | ||||
|         Default = -1, // Default behaviour for no value added in registry | ||||
|  | ||||
|         /// <summary> | ||||
|         /// No adornment | ||||
|         /// </summary> | ||||
|         None = 0, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Drop shadow adornment | ||||
|         /// </summary> | ||||
|         DropShadow = 1, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Photo border adornment | ||||
|         /// </summary> | ||||
|         PhotoBorder = 2, | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Video sprocket adornment | ||||
|         /// </summary> | ||||
|         VideoSprockets = 3 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								PhotoThumbnail.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								PhotoThumbnail.sln
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
|  | ||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio Version 17 | ||||
| VisualStudioVersion = 17.0.32014.148 | ||||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhotoThumbnail", "PhotoThumbnail\PhotoThumbnail.csproj", "{08C34C77-B778-46AA-A48B-BEA6B9FE07FE}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsoleApp", "TestConsoleApp\TestConsoleApp.csproj", "{C2573376-C9CF-41DE-B9A6-22790C048062}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| 		Release|Any CPU = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
| 		{08C34C77-B778-46AA-A48B-BEA6B9FE07FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{08C34C77-B778-46AA-A48B-BEA6B9FE07FE}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{08C34C77-B778-46AA-A48B-BEA6B9FE07FE}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{08C34C77-B778-46AA-A48B-BEA6B9FE07FE}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{C2573376-C9CF-41DE-B9A6-22790C048062}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C2573376-C9CF-41DE-B9A6-22790C048062}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{C2573376-C9CF-41DE-B9A6-22790C048062}.Release|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C2573376-C9CF-41DE-B9A6-22790C048062}.Release|Any CPU.Build.0 = Debug|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {40333A22-52A4-4082-B351-CD5B9550653E} | ||||
| 	EndGlobalSection | ||||
| EndGlobal | ||||
							
								
								
									
										73
									
								
								PhotoThumbnail/PhotoThumbnail.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								PhotoThumbnail/PhotoThumbnail.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||||
|   <PropertyGroup> | ||||
|     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||||
|     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||||
|     <ProjectGuid>{08C34C77-B778-46AA-A48B-BEA6B9FE07FE}</ProjectGuid> | ||||
|     <OutputType>Library</OutputType> | ||||
|     <AppDesignerFolder>Properties</AppDesignerFolder> | ||||
|     <RootNamespace>PhotoThumbnail</RootNamespace> | ||||
|     <AssemblyName>PhotoThumbnail</AssemblyName> | ||||
|     <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> | ||||
|     <FileAlignment>512</FileAlignment> | ||||
|     <Deterministic>true</Deterministic> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||||
|     <DebugSymbols>true</DebugSymbols> | ||||
|     <DebugType>full</DebugType> | ||||
|     <Optimize>false</Optimize> | ||||
|     <OutputPath>bin\Debug\</OutputPath> | ||||
|     <DefineConstants>DEBUG;TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||||
|     <DebugType>pdbonly</DebugType> | ||||
|     <Optimize>true</Optimize> | ||||
|     <OutputPath>bin\Release\</OutputPath> | ||||
|     <DefineConstants>TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup> | ||||
|     <SignAssembly>true</SignAssembly> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup> | ||||
|     <AssemblyOriginatorKeyFile>PhotoThumbnail.pfx</AssemblyOriginatorKeyFile> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Core" /> | ||||
|     <Reference Include="System.Drawing" /> | ||||
|     <Reference Include="System.Xml.Linq" /> | ||||
|     <Reference Include="System.Data.DataSetExtensions" /> | ||||
|     <Reference Include="Microsoft.CSharp" /> | ||||
|     <Reference Include="System.Data" /> | ||||
|     <Reference Include="System.Net.Http" /> | ||||
|     <Reference Include="System.Xml" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="..\Definitions\Reference.cs"> | ||||
|       <Link>Definitions\Reference.cs</Link> | ||||
|     </Compile> | ||||
|     <Compile Include="..\Definitions\StorageStream.cs"> | ||||
|       <Link>Definitions\StorageStream.cs</Link> | ||||
|     </Compile> | ||||
|     <Compile Include="..\Definitions\ThumbnailProvider.cs"> | ||||
|       <Link>Definitions\ThumbnailProvider.cs</Link> | ||||
|     </Compile> | ||||
|     <Compile Include="..\Definitions\ThumbnailProviderAttribute.cs"> | ||||
|       <Link>Definitions\ThumbnailProviderAttribute.cs</Link> | ||||
|     </Compile> | ||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
|     <Compile Include="TgaDecoder.cs" /> | ||||
|     <Compile Include="TgaThumbnailer.cs" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <None Include="PhotoThumbnail.pfx" /> | ||||
|   </ItemGroup> | ||||
|   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||||
| </Project> | ||||
							
								
								
									
										41
									
								
								PhotoThumbnail/Properties/AssemblyInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								PhotoThumbnail/Properties/AssemblyInfo.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| using System.Reflection; | ||||
| using System.Resources; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Security; | ||||
|  | ||||
| // General Information about an assembly is controlled through the following | ||||
| // set of attributes. Change these attribute values to modify the information | ||||
| // associated with an assembly. | ||||
| [assembly: AssemblyTitle("PhotoThumbnail")] | ||||
| [assembly: AssemblyDescription("")] | ||||
| [assembly: AssemblyConfiguration("")] | ||||
| [assembly: AssemblyCompany("")] | ||||
| [assembly: AssemblyProduct("PhotoThumbnail")] | ||||
| [assembly: AssemblyCopyright("Copyright ©  2021")] | ||||
| [assembly: AssemblyTrademark("")] | ||||
| [assembly: AssemblyCulture("")] | ||||
|  | ||||
| [assembly: SecurityRules(SecurityRuleSet.Level1)] | ||||
|  | ||||
| // Setting ComVisible to false makes the types in this assembly not visible | ||||
| // to COM components.  If you need to access a type in this assembly from | ||||
| // COM, set the ComVisible attribute to true on that type. | ||||
| [assembly: ComVisible(false)] | ||||
|  | ||||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | ||||
| [assembly: Guid("08c34c77-b778-46aa-a48b-bea6b9fe07fe")] | ||||
|  | ||||
| // Version information for an assembly consists of the following four values: | ||||
| // | ||||
| //      Major Version | ||||
| //      Minor Version | ||||
| //      Build Number | ||||
| //      Revision | ||||
| // | ||||
| // You can specify all the values or you can default the Build and Revision Numbers | ||||
| // by using the '*' as shown below: | ||||
| // [assembly: AssemblyVersion("1.0.*")] | ||||
| [assembly: AssemblyVersion("1.0.0.0")] | ||||
| [assembly: AssemblyFileVersion("1.0.0.0")] | ||||
|  | ||||
| [assembly: NeutralResourcesLanguage("en")] | ||||
							
								
								
									
										185
									
								
								PhotoThumbnail/TgaDecoder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								PhotoThumbnail/TgaDecoder.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | ||||
| using System; | ||||
| using System.Drawing; | ||||
| using System.IO; | ||||
|  | ||||
| namespace PhotoThumbnail | ||||
| { | ||||
|     internal class TgaDecoder | ||||
|     { | ||||
|         private class TgaData | ||||
|         { | ||||
|             private const int TgaHeaderSize = 18; | ||||
|  | ||||
|             //private int idFieldLength; | ||||
|             private readonly int colorMapType; | ||||
|             private readonly int imageType; | ||||
|             //private int colorMapIndex; | ||||
|             //private int colorMapLength; | ||||
|             //private int colorMapDepth; | ||||
|             //private int imageOriginX; | ||||
|             //private int imageOriginY; | ||||
|             private readonly int imageWidth; | ||||
|             private readonly int imageHeight; | ||||
|             private readonly int bitPerPixel; | ||||
|             private readonly int descriptor; | ||||
|             private readonly byte[] colorData; | ||||
|  | ||||
|             public TgaData(byte[] image) | ||||
|             { | ||||
|                 //idFieldLength = image[0]; | ||||
|                 colorMapType = image[1]; | ||||
|                 imageType = image[2]; | ||||
|                 //colorMapIndex = image[4] << 8 | image[3]; | ||||
|                 //colorMapLength = image[6] << 8 | image[5]; | ||||
|                 //colorMapDepth = image[7]; | ||||
|                 //imageOriginX = image[9] << 8 | image[8]; | ||||
|                 //imageOriginY = image[11] << 8 | image[10]; | ||||
|                 imageWidth = image[13] << 8 | image[12]; | ||||
|                 imageHeight = image[15] << 8 | image[14]; | ||||
|                 bitPerPixel = image[16]; | ||||
|                 descriptor = image[17]; | ||||
|                 colorData = new byte[image.Length - TgaHeaderSize]; | ||||
|                 Array.Copy(image, TgaHeaderSize, colorData, 0, colorData.Length); | ||||
|                 // Index color RLE or Full color RLE or Gray RLE | ||||
|                 if (imageType == 9 || imageType == 10 || imageType == 11) | ||||
|                     colorData = DecodeRLE(); | ||||
|             } | ||||
|  | ||||
|             public int Width => imageWidth; | ||||
|  | ||||
|             public int Height => imageHeight; | ||||
|  | ||||
|             public int GetPixel(int x, int y) | ||||
|             { | ||||
|                 if (colorMapType == 0) | ||||
|                 { | ||||
|                     switch (imageType) | ||||
|                     { | ||||
|                         // Index color | ||||
|                         case 1: | ||||
|                         case 9: | ||||
|                             // not implemented | ||||
|                             return 0; | ||||
|  | ||||
|                         // Full color | ||||
|                         case 2: | ||||
|                         case 10: | ||||
|                             int elementCount = bitPerPixel / 8; | ||||
|                             int dy = ((descriptor & 0x20) == 0 ? (imageHeight - 1 - y) : y) * (imageWidth * elementCount); | ||||
|                             int dx = ((descriptor & 0x10) == 0 ? x : (imageWidth - 1 - x)) * elementCount; | ||||
|                             int index = dy + dx; | ||||
|  | ||||
|                             int b = colorData[index + 0] & 0xFF; | ||||
|                             int g = colorData[index + 1] & 0xFF; | ||||
|                             int r = colorData[index + 2] & 0xFF; | ||||
|  | ||||
|                             if (elementCount == 4) // bitPerPixel == 32 | ||||
|                             { | ||||
|                                 int a = colorData[index + 3] & 0xFF; | ||||
|                                 return (a << 24) | (r << 16) | (g << 8) | b; | ||||
|                             } | ||||
|                             else if (elementCount == 3) // bitPerPixel == 24 | ||||
|                             { | ||||
|                                 return (r << 16) | (g << 8) | b; | ||||
|                             } | ||||
|                             break; | ||||
|  | ||||
|                         // Gray | ||||
|                         case 3: | ||||
|                         case 11: | ||||
|                             // not implemented | ||||
|                             return 0; | ||||
|                     } | ||||
|                     return 0; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // not implemented | ||||
|                     return 0; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             protected byte[] DecodeRLE() | ||||
|             { | ||||
|                 int elementCount = bitPerPixel / 8; | ||||
|                 byte[] elements = new byte[elementCount]; | ||||
|                 int decodeBufferLength = elementCount * imageWidth * imageHeight; | ||||
|                 byte[] decodeBuffer = new byte[decodeBufferLength]; | ||||
|                 int decoded = 0; | ||||
|                 int offset = 0; | ||||
|                 while (decoded < decodeBufferLength) | ||||
|                 { | ||||
|                     int packet = colorData[offset++] & 0xFF; | ||||
|                     if ((packet & 0x80) != 0) | ||||
|                     { | ||||
|                         for (int i = 0; i < elementCount; i++) | ||||
|                         { | ||||
|                             elements[i] = colorData[offset++]; | ||||
|                         } | ||||
|                         int count = (packet & 0x7F) + 1; | ||||
|                         for (int i = 0; i < count; i++) | ||||
|                         { | ||||
|                             for (int j = 0; j < elementCount; j++) | ||||
|                             { | ||||
|                                 decodeBuffer[decoded++] = elements[j]; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         int count = (packet + 1) * elementCount; | ||||
|                         for (int i = 0; i < count; i++) | ||||
|                         { | ||||
|                             decodeBuffer[decoded++] = colorData[offset++]; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 return decodeBuffer; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public static Bitmap FromStream(Stream stream) | ||||
|         { | ||||
|             using (var mstream = new MemoryStream()) | ||||
|             { | ||||
|                 stream.CopyTo(mstream); | ||||
|  | ||||
|                 return Decode(mstream.ToArray()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private unsafe static Bitmap Decode(byte[] image) | ||||
|         { | ||||
|             TgaData tga = new TgaData(image); | ||||
|             Rectangle rect = new Rectangle(0, 0, tga.Width, tga.Height); | ||||
|             Bitmap bitmap = new Bitmap(rect.Width, rect.Height); | ||||
|  | ||||
|             var data = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); | ||||
|             byte* p = (byte*)data.Scan0; | ||||
|             int offset = data.Stride - rect.Width * 4; | ||||
|  | ||||
|             for (var y = 0; y < tga.Height; y++) | ||||
|             { | ||||
|                 for (var x = 0; x < tga.Width; x++) | ||||
|                 { | ||||
|                     var c = BitConverter.GetBytes(tga.GetPixel(x, y)); | ||||
|                     if (c[3] == 0) | ||||
|                     { | ||||
|                         p += 4; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         *p++ = c[0]; | ||||
|                         *p++ = c[1]; | ||||
|                         *p++ = c[2]; | ||||
|                         *p++ = c[3]; | ||||
|                     } | ||||
|                 } | ||||
|                 p += offset; | ||||
|             } | ||||
|  | ||||
|             bitmap.UnlockBits(data); | ||||
|             return bitmap; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										44
									
								
								PhotoThumbnail/TgaThumbnailer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								PhotoThumbnail/TgaThumbnailer.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| using PhotoThumbnail.Definitions; | ||||
| using System; | ||||
| using System.Drawing; | ||||
| using System.IO; | ||||
| using System.Runtime.InteropServices; | ||||
|  | ||||
| namespace PhotoThumbnail | ||||
| { | ||||
|     [Guid("6f36dc76-d390-4042-b5d7-89e96e5dddc2")] | ||||
|     [ComVisible(true)] | ||||
|     [ClassInterface(ClassInterfaceType.None)] | ||||
|     [ProgId("PhotoThumbnail.TgaThumbnailer")] | ||||
|     [ThumbnailProvider("TgaThumbnailer", ".tga", ThumbnailAdornment = ThumbnailAdornment.PhotoBorder)] | ||||
|     public class TgaThumbnailer : ThumbnailProvider, IThumbnailFromStream | ||||
|     { | ||||
|         #region IThumbnailFromStream Members | ||||
|  | ||||
|         public Bitmap ConstructBitmap(Stream stream, int sideSize) | ||||
|         { | ||||
|             Bitmap bitmap = TgaDecoder.FromStream(stream); | ||||
|  | ||||
|             if (bitmap.Width > sideSize || bitmap.Height > sideSize) | ||||
|             { | ||||
|                 int w = bitmap.Width; | ||||
|                 int h = bitmap.Height; | ||||
|                 if (w > sideSize) | ||||
|                 { | ||||
|                     h = sideSize * h / w; | ||||
|                     w = sideSize; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     w = w * sideSize / h; | ||||
|                     h = sideSize; | ||||
|                 } | ||||
|                 bitmap = new Bitmap(bitmap, w, h); | ||||
|             } | ||||
|  | ||||
|             return bitmap; | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|     } | ||||
| } | ||||
							
								
								
									
										6
									
								
								TestConsoleApp/App.config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								TestConsoleApp/App.config
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8" ?> | ||||
| <configuration> | ||||
|     <startup>  | ||||
|         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> | ||||
|     </startup> | ||||
| </configuration> | ||||
							
								
								
									
										15
									
								
								TestConsoleApp/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								TestConsoleApp/Program.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| using PhotoThumbnail; | ||||
| using System.Drawing.Imaging; | ||||
| using System.IO; | ||||
|  | ||||
| namespace TestConsoleApp | ||||
| { | ||||
|     internal class Program | ||||
|     { | ||||
|         static void Main(string[] args) | ||||
|         { | ||||
|             var bitmap = TgaDecoder.FromStream(File.OpenRead(@"C:\Program Files (x86)\Steam\steamapps\common\wallpaper_engine\assets\presets\fern\materials\presets\fern1.tga")); | ||||
|             bitmap.Save(@"C:\Users\tsanie\Downloads\test.png", ImageFormat.Png); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										36
									
								
								TestConsoleApp/Properties/AssemblyInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								TestConsoleApp/Properties/AssemblyInfo.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| using System.Reflection; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Runtime.InteropServices; | ||||
|  | ||||
| // General Information about an assembly is controlled through the following | ||||
| // set of attributes. Change these attribute values to modify the information | ||||
| // associated with an assembly. | ||||
| [assembly: AssemblyTitle("TestConsoleApp")] | ||||
| [assembly: AssemblyDescription("")] | ||||
| [assembly: AssemblyConfiguration("")] | ||||
| [assembly: AssemblyCompany("")] | ||||
| [assembly: AssemblyProduct("TestConsoleApp")] | ||||
| [assembly: AssemblyCopyright("Copyright ©  2021")] | ||||
| [assembly: AssemblyTrademark("")] | ||||
| [assembly: AssemblyCulture("")] | ||||
|  | ||||
| // Setting ComVisible to false makes the types in this assembly not visible | ||||
| // to COM components.  If you need to access a type in this assembly from | ||||
| // COM, set the ComVisible attribute to true on that type. | ||||
| [assembly: ComVisible(false)] | ||||
|  | ||||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | ||||
| [assembly: Guid("c2573376-c9cf-41de-b9a6-22790c048062")] | ||||
|  | ||||
| // Version information for an assembly consists of the following four values: | ||||
| // | ||||
| //      Major Version | ||||
| //      Minor Version | ||||
| //      Build Number | ||||
| //      Revision | ||||
| // | ||||
| // You can specify all the values or you can default the Build and Revision Numbers | ||||
| // by using the '*' as shown below: | ||||
| // [assembly: AssemblyVersion("1.0.*")] | ||||
| [assembly: AssemblyVersion("1.0.0.0")] | ||||
| [assembly: AssemblyFileVersion("1.0.0.0")] | ||||
							
								
								
									
										59
									
								
								TestConsoleApp/TestConsoleApp.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								TestConsoleApp/TestConsoleApp.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||||
|   <PropertyGroup> | ||||
|     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||||
|     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||||
|     <ProjectGuid>{C2573376-C9CF-41DE-B9A6-22790C048062}</ProjectGuid> | ||||
|     <OutputType>Exe</OutputType> | ||||
|     <RootNamespace>TestConsoleApp</RootNamespace> | ||||
|     <AssemblyName>TestConsoleApp</AssemblyName> | ||||
|     <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> | ||||
|     <FileAlignment>512</FileAlignment> | ||||
|     <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> | ||||
|     <Deterministic>true</Deterministic> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||||
|     <PlatformTarget>AnyCPU</PlatformTarget> | ||||
|     <DebugSymbols>true</DebugSymbols> | ||||
|     <DebugType>full</DebugType> | ||||
|     <Optimize>false</Optimize> | ||||
|     <OutputPath>bin\Debug\</OutputPath> | ||||
|     <DefineConstants>DEBUG;TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||||
|     <PlatformTarget>AnyCPU</PlatformTarget> | ||||
|     <DebugType>pdbonly</DebugType> | ||||
|     <Optimize>true</Optimize> | ||||
|     <OutputPath>bin\Release\</OutputPath> | ||||
|     <DefineConstants>TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Core" /> | ||||
|     <Reference Include="System.Drawing" /> | ||||
|     <Reference Include="System.Xml.Linq" /> | ||||
|     <Reference Include="System.Data.DataSetExtensions" /> | ||||
|     <Reference Include="Microsoft.CSharp" /> | ||||
|     <Reference Include="System.Data" /> | ||||
|     <Reference Include="System.Net.Http" /> | ||||
|     <Reference Include="System.Xml" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="..\PhotoThumbnail\TgaDecoder.cs"> | ||||
|       <Link>TgaDecoder.cs</Link> | ||||
|     </Compile> | ||||
|     <Compile Include="Program.cs" /> | ||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <None Include="App.config" /> | ||||
|   </ItemGroup> | ||||
|   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||||
| </Project> | ||||
		Reference in New Issue
	
	Block a user