initial
This commit is contained in:
parent
5975f73cfb
commit
3a948f2063
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>
|
Loading…
x
Reference in New Issue
Block a user