diff --git a/PowerToys.sln b/PowerToys.sln index d6372908a2..80f5b0adbb 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -468,7 +468,21 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithLibInterop", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPOWrapper", "src\common\GPOWrapper\GPOWrapper.vcxproj", "{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPOWrapperProjection", "src\common\GPOWrapperProjection\GPOWrapperProjection.csproj", "{00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GPOWrapperProjection", "src\common\GPOWrapperProjection\GPOWrapperProjection.csproj", "{00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Peek", "Peek", "{17B4FA70-001E-4D33-BBBB-0D142DBC2E20}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peek.UI.WPF", "src\modules\peek\Peek.UI.WPF\Peek.UI.WPF.csproj", "{C0240BC3-95AF-4B38-811A-76E3FD56B576}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Peek", "src\modules\peek\peek\peek.vcxproj", "{A1425B53-3D61-4679-8623-E64A0D3D0A48}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peek.UI", "src\modules\peek\Peek.UI\Peek.UI.csproj", "{9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peek.Common", "src\modules\peek\Peek.Common\Peek.Common.csproj", "{17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peek.FilePreviewer", "src\modules\peek\Peek.FilePreviewer\Peek.FilePreviewer.csproj", "{AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WIC", "src\modules\peek\WIC\WIC.csproj", "{21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1909,6 +1923,84 @@ Global {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}.Release|x64.Build.0 = Release|x64 {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}.Release|x86.ActiveCfg = Release|x64 {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97}.Release|x86.Build.0 = Release|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Debug|ARM64.Build.0 = Debug|ARM64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Debug|x64.ActiveCfg = Debug|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Debug|x64.Build.0 = Debug|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Debug|x86.ActiveCfg = Debug|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Debug|x86.Build.0 = Debug|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Release|ARM64.ActiveCfg = Release|ARM64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Release|ARM64.Build.0 = Release|ARM64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Release|x64.ActiveCfg = Release|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Release|x64.Build.0 = Release|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Release|x86.ActiveCfg = Release|x64 + {C0240BC3-95AF-4B38-811A-76E3FD56B576}.Release|x86.Build.0 = Release|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Debug|ARM64.ActiveCfg = Debug|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Debug|ARM64.Build.0 = Debug|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Debug|x64.ActiveCfg = Debug|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Debug|x64.Build.0 = Debug|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Debug|x86.ActiveCfg = Debug|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Debug|x86.Build.0 = Debug|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Release|ARM64.ActiveCfg = Release|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Release|ARM64.Build.0 = Release|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Release|x64.ActiveCfg = Release|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Release|x64.Build.0 = Release|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Release|x86.ActiveCfg = Release|x64 + {A1425B53-3D61-4679-8623-E64A0D3D0A48}.Release|x86.Build.0 = Release|x64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|ARM64.Build.0 = Debug|ARM64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|x64.ActiveCfg = Debug|x64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|x64.Build.0 = Debug|x64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|x64.Deploy.0 = Debug|x64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|x86.ActiveCfg = Debug|x86 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|x86.Build.0 = Debug|x86 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Debug|x86.Deploy.0 = Debug|x86 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|ARM64.ActiveCfg = Release|ARM64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|ARM64.Build.0 = Release|ARM64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|ARM64.Deploy.0 = Release|ARM64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|x64.ActiveCfg = Release|x64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|x64.Build.0 = Release|x64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|x64.Deploy.0 = Release|x64 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|x86.ActiveCfg = Release|x86 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|x86.Build.0 = Release|x86 + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}.Release|x86.Deploy.0 = Release|x86 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Debug|ARM64.Build.0 = Debug|ARM64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Debug|x64.ActiveCfg = Debug|x64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Debug|x64.Build.0 = Debug|x64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Debug|x86.ActiveCfg = Debug|x64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Debug|x86.Build.0 = Debug|x64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Release|ARM64.ActiveCfg = Release|ARM64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Release|ARM64.Build.0 = Release|ARM64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Release|x64.ActiveCfg = Release|x64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Release|x64.Build.0 = Release|x64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Release|x86.ActiveCfg = Release|x64 + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB}.Release|x86.Build.0 = Release|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Debug|ARM64.Build.0 = Debug|ARM64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Debug|x64.ActiveCfg = Debug|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Debug|x64.Build.0 = Debug|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Debug|x86.ActiveCfg = Debug|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Debug|x86.Build.0 = Debug|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Release|ARM64.ActiveCfg = Release|ARM64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Release|ARM64.Build.0 = Release|ARM64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Release|x64.ActiveCfg = Release|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Release|x64.Build.0 = Release|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Release|x86.ActiveCfg = Release|x64 + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC}.Release|x86.Build.0 = Release|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Debug|ARM64.Build.0 = Debug|ARM64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Debug|x64.ActiveCfg = Debug|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Debug|x64.Build.0 = Debug|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Debug|x86.ActiveCfg = Debug|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Debug|x86.Build.0 = Debug|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Release|ARM64.ActiveCfg = Release|ARM64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Release|ARM64.Build.0 = Release|ARM64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Release|x64.ActiveCfg = Release|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Release|x64.Build.0 = Release|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Release|x86.ActiveCfg = Release|x64 + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2071,6 +2163,13 @@ Global {C604B37E-9D0E-4484-8778-E8B31B0E1B3A} = {AB82E5DD-C32D-4F28-9746-2C780846188E} {E599C30B-9DC8-4E5A-BF27-93D4CCEDE788} = {1AFB6476-670D-4E80-A464-657E01DFF482} {00EE9BA6-4E8F-43CA-960D-D4882F0FBB97} = {1AFB6476-670D-4E80-A464-657E01DFF482} + {17B4FA70-001E-4D33-BBBB-0D142DBC2E20} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} + {C0240BC3-95AF-4B38-811A-76E3FD56B576} = {17B4FA70-001E-4D33-BBBB-0D142DBC2E20} + {A1425B53-3D61-4679-8623-E64A0D3D0A48} = {17B4FA70-001E-4D33-BBBB-0D142DBC2E20} + {9D7A6DE0-7D27-424D-ABAE-41B2161F9A03} = {17B4FA70-001E-4D33-BBBB-0D142DBC2E20} + {17A99C7C-0BFF-45BB-A9FD-63A0DDC105BB} = {17B4FA70-001E-4D33-BBBB-0D142DBC2E20} + {AA9F0AF8-7924-4D59-BAA1-E36F1304E0DC} = {17B4FA70-001E-4D33-BBBB-0D142DBC2E20} + {21B69DE5-59FD-4C5D-A142-EF1C1C430EAF} = {17B4FA70-001E-4D33-BBBB-0D142DBC2E20} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 2a96496591..5b9491858f 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -17,6 +17,7 @@ + @@ -53,7 +54,7 @@ - + @@ -542,6 +543,12 @@ + + + + + + @@ -1032,6 +1039,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1332,6 +1364,9 @@ + + + @@ -1407,7 +1442,7 @@ - + @@ -1561,6 +1596,12 @@ Guid="$(var.CompGUIDPrefix)03"> + + + processesToTerminate = { + std::array processesToTerminate = { L"PowerToys.PowerLauncher.exe", L"PowerToys.Settings.exe", L"PowerToys.Awake.exe", @@ -1239,6 +1239,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall) L"PowerToys.FileLocksmithUI.exe", L"PowerToys.ColorPickerUI.exe", L"PowerToys.AlwaysOnTop.exe", + L"PowerToys.PeekUI.exe", L"PowerToys.exe" }; diff --git a/src/common/interop/interop.cpp b/src/common/interop/interop.cpp index 83f48732f8..6b8e5d1c00 100644 --- a/src/common/interop/interop.cpp +++ b/src/common/interop/interop.cpp @@ -203,6 +203,10 @@ public return gcnew String(CommonSharedConstants::AWAKE_EXIT_EVENT); } + static String^ ShowPeekEvent() { + return gcnew String(CommonSharedConstants::SHOW_PEEK_SHARED_EVENT); + } + static String ^ PowerAccentExitEvent() { return gcnew String(CommonSharedConstants::POWERACCENT_EXIT_EVENT); } diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index 7adf617047..71381e35fe 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -44,6 +44,9 @@ namespace CommonSharedConstants // Path to the event used by PowerOCR const wchar_t SHOW_POWEROCR_SHARED_EVENT[] = L"Local\\PowerOCREvent-dc864e06-e1af-4ecc-9078-f98bee745e3a"; + // Path to the event used to show Peek + const wchar_t SHOW_PEEK_SHARED_EVENT[] = L"Local\\ShowPeekEvent"; + // Max DWORD for key code to disable keys. const DWORD VK_DISABLED = 0x100; } diff --git a/src/modules/peek/Peek.Common/Models/File.cs b/src/modules/peek/Peek.Common/Models/File.cs new file mode 100644 index 0000000000..58ab722a05 --- /dev/null +++ b/src/modules/peek/Peek.Common/Models/File.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.Common.Models +{ + using System; + using System.Threading.Tasks; + using Windows.Storage; + +#nullable enable + + public class File + { + private StorageFile? storageFile; + + public File(string path) + { + Path = path; + } + + public string Path { get; init; } + + public string Extension => System.IO.Path.GetExtension(Path); + + public async Task GetStorageFileAsync() + { + if (storageFile == null) + { + storageFile = await StorageFile.GetFileFromPathAsync(Path); + } + + return storageFile; + } + } +} diff --git a/src/modules/peek/Peek.Common/Models/HResult.cs b/src/modules/peek/Peek.Common/Models/HResult.cs new file mode 100644 index 0000000000..57768d3cdb --- /dev/null +++ b/src/modules/peek/Peek.Common/Models/HResult.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.Common.Models +{ + public enum HResult + { + Ok = 0x0000, + False = 0x0001, + InvalidArguments = unchecked((int)0x80070057), + OutOfMemory = unchecked((int)0x8007000E), + NoInterface = unchecked((int)0x80004002), + Fail = unchecked((int)0x80004005), + ExtractionFailed = unchecked((int)0x8004B200), + ElementNotFound = unchecked((int)0x80070490), + TypeElementNotFound = unchecked((int)0x8002802B), + NoObject = unchecked((int)0x800401E5), + Win32ErrorCanceled = 1223, + Canceled = unchecked((int)0x800704C7), + ResourceInUse = unchecked((int)0x800700AA), + AccessDenied = unchecked((int)0x80030005), + } +} diff --git a/src/modules/peek/Peek.Common/Models/IShellItem.cs b/src/modules/peek/Peek.Common/Models/IShellItem.cs new file mode 100644 index 0000000000..8fd1fa2440 --- /dev/null +++ b/src/modules/peek/Peek.Common/Models/IShellItem.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.Common.Models +{ + using System; + using System.Runtime.InteropServices; + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] + public interface IShellItem + { + void BindToHandler( + IntPtr pbc, + [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, + [MarshalAs(UnmanagedType.LPStruct)] Guid riid, + out IntPtr ppv); + + void GetParent(out IShellItem ppsi); + + void GetDisplayName(Sigdn sigdnName, out IntPtr ppszName); + + void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); + + void Compare(IShellItem psi, uint hint, out int piOrder); + } + + public enum Sigdn : uint + { + NormalDisplay = 0, + ParentRelativeParsing = 0x80018001, + ParentRelativeForAddressBar = 0x8001c001, + DesktopAbsoluteParsing = 0x80028000, + ParentRelativeEditing = 0x80031001, + DesktopAbsoluteEditing = 0x8004c000, + FileSysPath = 0x80058000, + Url = 0x80068000, + } +} diff --git a/src/modules/peek/Peek.Common/Models/IShellItemImageFactory.cs b/src/modules/peek/Peek.Common/Models/IShellItemImageFactory.cs new file mode 100644 index 0000000000..42920aabd3 --- /dev/null +++ b/src/modules/peek/Peek.Common/Models/IShellItemImageFactory.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.Common.Models +{ + using System; + using System.Runtime.InteropServices; + + [ComImport] + [Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IShellItemImageFactory + { + [PreserveSig] + HResult GetImage( + [In, MarshalAs(UnmanagedType.Struct)] NativeSize size, + [In] ThumbnailOptions flags, + [Out] out IntPtr phbm); + } + + [StructLayout(LayoutKind.Sequential)] + public struct NativeSize + { + private int width; + private int height; + + public int Width + { + set { width = value; } + } + + public int Height + { + set { height = value; } + } + } + + [Flags] + public enum ThumbnailOptions + { + None = 0x00, + BiggerSizeOk = 0x01, + InMemoryOnly = 0x02, + IconOnly = 0x04, + ThumbnailOnly = 0x08, + InCacheOnly = 0x10, + ScaleUp = 0x100, + } +} diff --git a/src/modules/peek/Peek.Common/Peek.Common.csproj b/src/modules/peek/Peek.Common/Peek.Common.csproj new file mode 100644 index 0000000000..04afd55aa5 --- /dev/null +++ b/src/modules/peek/Peek.Common/Peek.Common.csproj @@ -0,0 +1,15 @@ + + + net7.0-windows10.0.19041.0 + 10.0.17763.0 + Peek.Common + win10-x86;win10-x64;win10-arm64 + true + enable + + + + + + + diff --git a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml new file mode 100644 index 0000000000..05dabb2a3a --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml.cs b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml.cs new file mode 100644 index 0000000000..3ff252f2a4 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.FilePreviewer +{ + using System; + using System.Threading.Tasks; + using CommunityToolkit.Mvvm.ComponentModel; + using CommunityToolkit.WinUI.UI.Media.Pipelines; + using Microsoft.UI.Xaml; + using Microsoft.UI.Xaml.Controls; + using Microsoft.UI.Xaml.Media.Imaging; + using Peek.Common.Models; + using Peek.FilePreviewer.Models; + using Peek.FilePreviewer.Previewers; + using Windows.Foundation; + + [INotifyPropertyChanged] + public sealed partial class FilePreview : UserControl + { + public event EventHandler? PreviewSizeChanged; + + public static readonly DependencyProperty FilesProperty = + DependencyProperty.Register( + nameof(File), + typeof(File), + typeof(FilePreview), + new PropertyMetadata(false, async (d, e) => await ((FilePreview)d).OnFilePropertyChanged())); + + [ObservableProperty] + private ImagePreviewer? previewer; + + public FilePreview() + { + InitializeComponent(); + } + + public File File + { + get => (File)GetValue(FilesProperty); + set => SetValue(FilesProperty, value); + } + + public bool IsPreviewLoading(BitmapSource? bitmapSource) + { + return bitmapSource == null; + } + + private async Task OnFilePropertyChanged() + { + if (File == null) + { + return; + } + + // TODO: Implement plugin pattern to support any file types. + if (IsSupportedImage(File.Extension)) + { + Previewer = new ImagePreviewer(File); + var size = await Previewer.GetPreviewSizeAsync(); + PreviewSizeChanged?.Invoke(this, new PreviewSizeChangedArgs(size)); + await Previewer.LoadPreviewAsync(); + } + else + { + Previewer = null; + PreviewSizeChanged?.Invoke(this, new PreviewSizeChangedArgs(new Size(1280, 720))); + } + } + + // TODO: Find all supported file types for the image previewer + private static bool IsSupportedImage(string extension) => extension switch + { + ".bmp" => true, + ".gif" => true, + ".jpg" => true, + ".jfif" => true, + ".jfi" => true, + ".jif" => true, + ".jpeg" => true, + ".jpe" => true, + ".png" => true, + ".tif" => true, + ".tiff" => true, + _ => false, + }; + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Models/PreviewSizeChangedArgs.cs b/src/modules/peek/Peek.FilePreviewer/Models/PreviewSizeChangedArgs.cs new file mode 100644 index 0000000000..a34785e7cb --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Models/PreviewSizeChangedArgs.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.FilePreviewer.Models +{ + using Windows.Foundation; + + public class PreviewSizeChangedArgs + { + public PreviewSizeChangedArgs(Size windowSizeRequested) + { + WindowSizeRequested = windowSizeRequested; + } + + public Size WindowSizeRequested { get; init; } + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj b/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj new file mode 100644 index 0000000000..ac6bcb542e --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj @@ -0,0 +1,33 @@ + + + net7.0-windows10.0.19041.0 + 10.0.17763.0 + Peek.FilePreviewer + win10-x86;win10-x64;win10-arm64 + true + enable + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + + + diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/NativeMethods.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/NativeMethods.cs new file mode 100644 index 0000000000..eca34b8686 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/NativeMethods.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.Common +{ + using System; + using System.Runtime.InteropServices; + using Peek.Common.Models; + + public static class NativeMethods + { + [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int SHCreateItemFromParsingName( + [MarshalAs(UnmanagedType.LPWStr)] string path, + IntPtr pbc, + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); + + [DllImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool DeleteObject(IntPtr hObject); + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/ThumbnailHelper.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/ThumbnailHelper.cs new file mode 100644 index 0000000000..cfa2631c8d --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/ThumbnailHelper.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.FilePreviewer.Previewers +{ + using System; + using System.Collections.Generic; + using System.Runtime.InteropServices; + using Peek.Common; + using Peek.Common.Models; + + public static class ThumbnailHelper + { + // Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows + private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93"; + + public static readonly NativeSize HighQualityThumbnailSize = new NativeSize { Width = 720, Height = 720, }; + public static readonly NativeSize LowQualityThumbnailSize = new NativeSize { Width = 256, Height = 256, }; + + private static readonly NativeSize FallBackThumbnailSize = new NativeSize { Width = 96, Height = 96, }; + private static readonly NativeSize LastFallBackThumbnailSize = new NativeSize { Width = 32, Height = 32, }; + + private static readonly List ThumbnailFallBackSizes = new List + { + HighQualityThumbnailSize, + LowQualityThumbnailSize, + FallBackThumbnailSize, + LastFallBackThumbnailSize, + }; + + // TODO: Add a re-try system if there is no thumbnail of requested size. + public static HResult GetThumbnail(string filename, out IntPtr hbitmap, NativeSize thumbnailSize) + { + Guid shellItem2Guid = new Guid(IShellItem2Guid); + int retCode = NativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, ref shellItem2Guid, out IShellItem nativeShellItem); + + if (retCode != 0) + { + throw Marshal.GetExceptionForHR(retCode)!; + } + + var options = ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.ThumbnailOnly | ThumbnailOptions.ScaleUp; + + HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(thumbnailSize, options, out hbitmap); + + // Try to get thumbnail using the fallback sizes order + if (hr != HResult.Ok) + { + var currentThumbnailFallBackIndex = ThumbnailFallBackSizes.IndexOf(thumbnailSize); + var nextThumbnailFallBackIndex = currentThumbnailFallBackIndex + 1; + if (nextThumbnailFallBackIndex < ThumbnailFallBackSizes.Count - 1) + { + hr = GetThumbnail(filename, out hbitmap, ThumbnailFallBackSizes[nextThumbnailFallBackIndex]); + } + } + + Marshal.ReleaseComObject(nativeShellItem); + + return hr; + } + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/WICHelper.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/WICHelper.cs new file mode 100644 index 0000000000..31af196341 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/Helpers/WICHelper.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.FilePreviewer.Previewers +{ + using System; + using System.Threading.Tasks; + using WIC; + + public static class WICHelper + { + public static Task GetImageSize(string filePath) + { + return Task.Run(() => + { + // TODO: Find a way to get file metadata without hydrating files. Look into Shell API/Windows Property System, e.g., IPropertyStore + IWICImagingFactory factory = (IWICImagingFactory)new WICImagingFactoryClass(); + var decoder = factory.CreateDecoderFromFilename(filePath, IntPtr.Zero, StreamAccessMode.GENERIC_READ, WICDecodeOptions.WICDecodeMetadataCacheOnLoad); + var frame = decoder?.GetFrame(0); + int width = 0; + int height = 0; + + // TODO: Respect EXIF data and find correct orientation + frame?.GetSize(out width, out height); + + return new Windows.Foundation.Size(width, height); + }); + } + } +} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs new file mode 100644 index 0000000000..05455f7ad9 --- /dev/null +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/ImagePreviewer/ImagePreviewer.cs @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.FilePreviewer.Previewers +{ + using System; + using System.Drawing.Imaging; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using CommunityToolkit.Mvvm.ComponentModel; + using Microsoft.UI.Dispatching; + using Microsoft.UI.Xaml.Media.Imaging; + using Peek.Common; + using Windows.Foundation; + using File = Peek.Common.Models.File; + + [INotifyPropertyChanged] + public partial class ImagePreviewer : IDisposable + { + [ObservableProperty] + private BitmapSource? preview; + + public ImagePreviewer(File file) + { + File = file; + Dispatcher = DispatcherQueue.GetForCurrentThread(); + } + + private File File { get; } + + private DispatcherQueue Dispatcher { get; } + + private bool IsHighQualityThumbnailLoaded { get; set; } + + private bool IsFullImageLoaded { get; set; } + + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + + private CancellationToken CancellationToken => _cancellationTokenSource.Token; + + public void Dispose() + { + _cancellationTokenSource.Dispose(); + GC.SuppressFinalize(this); + } + + public Task GetPreviewSizeAsync() + { + return WICHelper.GetImageSize(File.Path); + } + + public Task LoadPreviewAsync() + { + var lowQualityThumbnailTask = LoadLowQualityThumbnailAsync(); + var highQualityThumbnailTask = LoadHighQualityThumbnailAsync(); + var fullImageTask = LoadFullQualityImageAsync(); + + return Task.WhenAll(lowQualityThumbnailTask, highQualityThumbnailTask, fullImageTask); + } + + private Task LoadLowQualityThumbnailAsync() + { + var thumbnailTCS = new TaskCompletionSource(); + Dispatcher.TryEnqueue(async () => + { + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + if (!IsFullImageLoaded && !IsHighQualityThumbnailLoaded) + { + // TODO: Handle thumbnail errors + ThumbnailHelper.GetThumbnail(Path.GetFullPath(File.Path), out IntPtr hbitmap, ThumbnailHelper.LowQualityThumbnailSize); + var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap); + Preview = thumbnailBitmap; + } + + thumbnailTCS.SetResult(); + }); + + return thumbnailTCS.Task; + } + + private Task LoadHighQualityThumbnailAsync() + { + var thumbnailTCS = new TaskCompletionSource(); + Dispatcher.TryEnqueue(async () => + { + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + if (!IsFullImageLoaded) + { + // TODO: Handle thumbnail errors + ThumbnailHelper.GetThumbnail(Path.GetFullPath(File.Path), out IntPtr hbitmap, ThumbnailHelper.HighQualityThumbnailSize); + var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap); + IsHighQualityThumbnailLoaded = true; + Preview = thumbnailBitmap; + } + + thumbnailTCS.SetResult(); + }); + + return thumbnailTCS.Task; + } + + private Task LoadFullQualityImageAsync() + { + var fullImageTCS = new TaskCompletionSource(); + Dispatcher.TryEnqueue(async () => + { + // TODO: Check if this is performant + var bitmap = await GetFullBitmapFromPathAsync(File.Path); + IsFullImageLoaded = true; + + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + Preview = bitmap; + fullImageTCS.SetResult(); + }); + + return fullImageTCS.Task; + } + + private static async Task GetFullBitmapFromPathAsync(string path) + { + var bitmap = new BitmapImage(); + using (FileStream stream = System.IO.File.OpenRead(path)) + { + await bitmap.SetSourceAsync(stream.AsRandomAccessStream()); + } + + return bitmap; + } + + private static async Task GetBitmapFromHBitmapAsync(IntPtr hbitmap) + { + try + { + var bitmap = System.Drawing.Image.FromHbitmap(hbitmap); + var bitmapImage = new BitmapImage(); + using (var stream = new MemoryStream()) + { + bitmap.Save(stream, ImageFormat.Bmp); + stream.Position = 0; + await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream()); + } + + return bitmapImage; + } + finally + { + // delete HBitmap to avoid memory leaks + NativeMethods.DeleteObject(hbitmap); + } + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/App.xaml b/src/modules/peek/Peek.UI.WPF/App.xaml new file mode 100644 index 0000000000..1fc9407448 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/App.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/App.xaml.cs b/src/modules/peek/Peek.UI.WPF/App.xaml.cs new file mode 100644 index 0000000000..54225caef6 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/App.xaml.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using System.Windows; +using Common.UI; +using ManagedCommon; + +namespace Peek.UI +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application, IDisposable + { + private ThemeManager? _themeManager; + + private Mutex? _instanceMutex; + private static string[] _args = Array.Empty(); + private int _powerToysRunnerPid; + private bool disposedValue; + + // TODO: Make sure no window appears or blinks at startup + protected override void OnStartup(StartupEventArgs e) + { + _args = e?.Args ?? Array.Empty(); + + // allow only one instance of peek + _instanceMutex = new Mutex(true, @"Local\PowerToys_Peek_InstanceMutex", out bool createdNew); + if (!createdNew) + { + _instanceMutex = null; + Environment.Exit(0); + return; + } + + if (_args?.Length > 0) + { + _ = int.TryParse(_args[0], out _powerToysRunnerPid); + + RunnerHelper.WaitForPowerToysRunner(_powerToysRunnerPid, () => + { + Environment.Exit(0); + }); + } + else + { + _powerToysRunnerPid = -1; + } + + _themeManager = new ThemeManager(this); + base.OnStartup(e); + } + + protected override void OnExit(ExitEventArgs e) + { + if (_instanceMutex != null) + { + _instanceMutex.ReleaseMutex(); + } + + base.OnExit(e); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _instanceMutex?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(true); + GC.SuppressFinalize(this); + } + + public bool IsRunningDetachedFromPowerToys() + { + return _powerToysRunnerPid == -1; + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/AssemblyInfo.cs b/src/modules/peek/Peek.UI.WPF/AssemblyInfo.cs new file mode 100644 index 0000000000..bcac370d7d --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/AssemblyInfo.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, // where theme specific resource dictionaries are located (used if a resource is not found in the page, or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly) // where the generic resource dictionary is locate (used if a resource is not found in the page, app, or any theme specific resource dictionaries) +] diff --git a/src/modules/peek/Peek.UI.WPF/Assets/error.png b/src/modules/peek/Peek.UI.WPF/Assets/error.png new file mode 100644 index 0000000000..c2eb2fc2df Binary files /dev/null and b/src/modules/peek/Peek.UI.WPF/Assets/error.png differ diff --git a/src/modules/peek/Peek.UI.WPF/Extensions/LinkedListNodeExtensions.cs b/src/modules/peek/Peek.UI.WPF/Extensions/LinkedListNodeExtensions.cs new file mode 100644 index 0000000000..5e740726e4 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Extensions/LinkedListNodeExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Peek.UI.Extensions +{ + public static class LinkedListNodeExtensions + { + public static LinkedListNode? GetNextOrFirst(this LinkedListNode current) + { + return current.Next ?? current.List?.First; + } + + public static LinkedListNode? GetPreviousOrLast(this LinkedListNode current) + { + return current.Previous ?? current.List?.Last; + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Extensions/SizeExtensions.cs b/src/modules/peek/Peek.UI.WPF/Extensions/SizeExtensions.cs new file mode 100644 index 0000000000..19ac26c8b9 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Extensions/SizeExtensions.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows; + +namespace Peek.UI.Extensions +{ + public static class SizeExtensions + { + public static Rect Fit(this Size sizeToFit, Rect bounds, Size maxSize, Size minSize, Size allowedGap, double reservedHeight) + { + double resultingWidth = sizeToFit.Width; + double resultingHeight = sizeToFit.Height; + + var ratioWidth = sizeToFit.Width / maxSize.Width; + var ratioHeight = sizeToFit.Height / maxSize.Height; + + if (ratioWidth > ratioHeight) + { + if (ratioWidth > 1) + { + resultingWidth = maxSize.Width; + resultingHeight = sizeToFit.Height / ratioWidth; + } + } + else + { + if (ratioHeight > 1) + { + resultingWidth = sizeToFit.Width / ratioHeight; + resultingHeight = maxSize.Height; + } + } + + if (resultingWidth < minSize.Width - allowedGap.Width) + { + resultingWidth = minSize.Width; + } + + if (resultingHeight < minSize.Height - allowedGap.Height) + { + resultingHeight = minSize.Height; + } + + resultingHeight += reservedHeight; + + // Calculate offsets to center content + double offsetX = (maxSize.Width - resultingWidth) / 2; + double offsetY = (maxSize.Height - resultingHeight) / 2; + + var maxWindowLeft = bounds.Left + ((bounds.Right - bounds.Left - maxSize.Width) / 2); + var maxWindowTop = bounds.Top + ((bounds.Bottom - bounds.Top - maxSize.Height) / 2); + + var resultingLeft = maxWindowLeft + offsetX; + var resultingTop = maxWindowTop + offsetY; + + return new Rect(resultingLeft, resultingTop, resultingWidth, resultingHeight); + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Extensions/WindowExtensions.cs b/src/modules/peek/Peek.UI.WPF/Extensions/WindowExtensions.cs new file mode 100644 index 0000000000..17abcda12c --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Extensions/WindowExtensions.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Windows; +using System.Windows.Interop; +using Peek.UI.Native; +using static Peek.UI.Native.NativeModels; + +namespace Peek.UI.Extensions +{ + public static class WindowExtensions + { + public static void SetToolStyle(this Window window) + { + var handle = new WindowInteropHelper(window).Handle; + _ = NativeMethods.SetWindowLong(handle, GwlExStyle, NativeMethods.GetWindowLong(handle, GwlExStyle) | WsExToolWindow); + } + + public static void BringToForeground(this Window window) + { + // Use SendInput hack to allow Activate to work - required to resolve focus issue https://github.com/microsoft/PowerToys/issues/4270 + Input input = new Input { Type = InputType.InputMouse, Data = { } }; + Input[] inputs = new Input[] { input }; + + // Send empty mouse event. This makes this thread the last to send input, and hence allows it to pass foreground permission checks + _ = NativeMethods.SendInput(1, inputs, Input.Size); + + window.Activate(); + } + + public static void RoundCorners(this Window window) + { + IntPtr hWnd = new System.Windows.Interop.WindowInteropHelper(Window.GetWindow(window)).EnsureHandle(); + var attribute = DwmWindowAttributed.DwmaWindowCornerPreference; + var preference = DwmWindowCornerPreference.DwmCpRound; + NativeMethods.DwmSetWindowAttribute(hWnd, attribute, ref preference, sizeof(uint)); + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Helpers/FileExplorerHelper.cs b/src/modules/peek/Peek.UI.WPF/Helpers/FileExplorerHelper.cs new file mode 100644 index 0000000000..c5262a3279 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Helpers/FileExplorerHelper.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Peek.UI.Helpers +{ + public static class FileExplorerHelper + { + public static IEnumerable GetSelectedItems(IntPtr handle) + { + var selectedItems = new List(); + var shell = new Shell32.Shell(); + foreach (SHDocVw.InternetExplorer window in shell.Windows()) + { + if (window.HWND == (int)handle) + { + Shell32.FolderItems items = ((Shell32.IShellFolderViewDual2)window.Document).SelectedItems(); + if (items != null && items.Count > 0) + { + foreach (Shell32.FolderItem item in items) + { + selectedItems.Add(item.Path); + } + } + } + } + + return selectedItems; + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Helpers/FileLoadHelper.cs b/src/modules/peek/Peek.UI.WPF/Helpers/FileLoadHelper.cs new file mode 100644 index 0000000000..e43e7d72c2 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Helpers/FileLoadHelper.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media.Imaging; +using Peek.UI.Models; + +namespace Peek.UI.Helpers +{ + public static class FileLoadHelper + { + public static Task LoadDimensionsAsync(string filename) + { + return Task.Run(() => + { + Size size = new Size(0, 0); + try + { + using (FileStream stream = File.OpenRead(filename)) + { + string extension = Path.GetExtension(stream.Name); + if (FileTypeHelper.IsSupportedImage(extension)) + { + using (System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(stream, false, false)) + { + var rotation = EvaluateRotationToApply(sourceImage); + if (rotation == Rotation.Rotate90 || rotation == Rotation.Rotate270) + { + size = new Size(sourceImage.Height, sourceImage.Width); + } + else + { + size = new Size(sourceImage.Width, sourceImage.Height); + } + + return Task.FromResult(new DimensionData { Size = size, Rotation = rotation }); + } + } + else + { + return Task.FromResult(new DimensionData { Size = size, Rotation = Rotation.Rotate0 }); + } + } + } + catch (Exception) + { + return Task.FromResult(new DimensionData { Size = size, Rotation = Rotation.Rotate0 }); + } + }); + } + + public static async Task LoadThumbnailAsync(string filename, bool iconFallback) + { + var thumbnail = await Task.Run(() => + { + var bitmapSource = ThumbnailHelper.GetThumbnail(filename, iconFallback); + bitmapSource.Freeze(); + return bitmapSource; + }); + + return thumbnail; + } + + public static Task LoadIconAsync(string filename) + { + return Task.Run(() => + { + var bitmapSource = ThumbnailHelper.GetIcon(filename); + bitmapSource.Freeze(); + return bitmapSource; + }); + } + + public static Task LoadFullImageAsync(string filename, Rotation rotation) + { + return Task.Run(() => + { + var bitmap = new BitmapImage(); + bitmap.BeginInit(); + bitmap.CacheOption = BitmapCacheOption.OnLoad; + bitmap.UriSource = new Uri(filename); + bitmap.Rotation = rotation; + bitmap.EndInit(); + bitmap.Freeze(); + return bitmap; + }); + } + + private static Rotation EvaluateRotationToApply(System.Drawing.Image image) + { + PropertyItem? property = image.PropertyItems?.FirstOrDefault(p => p.Id == 274); + + if (property != null && property.Value != null && property.Value.Length > 0) + { + int orientation = property.Value[0]; + + if (orientation == 6) + { + return Rotation.Rotate90; + } + + if (orientation == 3) + { + return Rotation.Rotate180; + } + + if (orientation == 8) + { + return Rotation.Rotate270; + } + } + + return Rotation.Rotate0; + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Helpers/FileTypeHelper.cs b/src/modules/peek/Peek.UI.WPF/Helpers/FileTypeHelper.cs new file mode 100644 index 0000000000..40b6a2ad23 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Helpers/FileTypeHelper.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Peek.UI.Native; +using static Peek.UI.Native.NativeModels; + +namespace Peek.UI.Helpers +{ + public static class FileTypeHelper + { + public static bool IsSupportedImage(string extension) => extension switch + { + ".bmp" => true, + ".gif" => true, + ".jpg" => true, + ".jfif" => true, + ".jfi" => true, + ".jif" => true, + ".jpeg" => true, + ".jpe" => true, + ".png" => true, + ".tif" => true, + ".tiff" => true, + _ => false, + }; + + public static bool IsMedia(string extension) + { + return IsImage(extension) || IsVideo(extension); + } + + public static bool IsImage(string extension) + { + return IsPerceivedType(extension, PerceivedType.Image); + } + + public static bool IsVideo(string extension) + { + return IsPerceivedType(extension, PerceivedType.Video); + } + + public static bool IsDocument(string extension) + { + return IsPerceivedType(extension, PerceivedType.Document); + } + + internal static bool IsPerceivedType(string extension, PerceivedType perceivedType) + { + if (string.IsNullOrEmpty(extension)) + { + return false; + } + + PerceivedType perceived; + Perceived flag; + bool isPerceivedType = false; + + try + { + if (NativeMethods.AssocGetPerceivedType(extension, out perceived, out flag, IntPtr.Zero) == HResult.Ok) + { + isPerceivedType = perceived == perceivedType; + } + } + catch (Exception) + { + // TODO: AssocGetPerceivedType throws on some file types (json, ps1, exe, etc.) + // Properly handle these + return false; + } + + return isPerceivedType; + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Helpers/ThumbnailHelper.cs b/src/modules/peek/Peek.UI.WPF/Helpers/ThumbnailHelper.cs new file mode 100644 index 0000000000..77a9bcf099 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Helpers/ThumbnailHelper.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Media.Imaging; +using Peek.UI.Models; +using Peek.UI.Native; +using static Peek.UI.Native.NativeModels; + +namespace Peek.UI.Helpers +{ + public static class ThumbnailHelper + { + private static readonly Assembly Assembly = Assembly.GetExecutingAssembly(); + public static readonly string ProgramDirectory = Directory.GetParent(Assembly.Location)!.ToString(); + public static readonly string ErrorIcon = Path.Combine(ProgramDirectory, "Assets", "error.png"); + + // Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows + private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93"; + + public static BitmapSource GetIcon(string fileName) + { + IntPtr hbitmap; + HResult hr = GetIconImpl(Path.GetFullPath(fileName), out hbitmap); + + if (hr != HResult.Ok) + { + return new BitmapImage(new Uri(ErrorIcon)); + } + + try + { + return Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); + } + finally + { + // delete HBitmap to avoid memory leaks + NativeMethods.DeleteObject(hbitmap); + } + } + + public static BitmapSource GetThumbnail(string fileName, bool iconFallback) + { + IntPtr hbitmap; + HResult hr = GetThumbnailImpl(Path.GetFullPath(fileName), out hbitmap); + + if (hr != HResult.Ok && iconFallback) + { + return GetIcon(fileName); + } + + try + { + return Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); + } + finally + { + // delete HBitmap to avoid memory leaks + NativeMethods.DeleteObject(hbitmap); + } + } + + private static HResult GetIconImpl(string filename, out IntPtr hbitmap) + { + Guid shellItem2Guid = new Guid(IShellItem2Guid); + int retCode = NativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, ref shellItem2Guid, out IShellItem nativeShellItem); + + if (retCode != 0) + { + throw Marshal.GetExceptionForHR(retCode)!; + } + + NativeSize large = new NativeSize { Width = 256, Height = 256 }; + var options = ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.IconOnly; + + HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(large, options, out hbitmap); + + Marshal.ReleaseComObject(nativeShellItem); + + return hr; + } + + private static HResult GetThumbnailImpl(string filename, out IntPtr hbitmap) + { + Guid shellItem2Guid = new Guid(IShellItem2Guid); + int retCode = NativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, ref shellItem2Guid, out IShellItem nativeShellItem); + + if (retCode != 0) + { + throw Marshal.GetExceptionForHR(retCode)!; + } + + var extraLarge = new NativeSize { Width = 1024, Height = 1024, }; + var large = new NativeSize { Width = 256, Height = 256 }; + var medium = new NativeSize { Width = 96, Height = 96 }; + var small = new NativeSize { Width = 32, Height = 32 }; + + var options = ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.ThumbnailOnly | ThumbnailOptions.ScaleUp; + + HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(extraLarge, options, out hbitmap); + + if (hr != HResult.Ok) + { + hr = ((IShellItemImageFactory)nativeShellItem).GetImage(large, options, out hbitmap); + } + + if (hr != HResult.Ok) + { + hr = ((IShellItemImageFactory)nativeShellItem).GetImage(medium, options, out hbitmap); + } + + if (hr != HResult.Ok) + { + hr = ((IShellItemImageFactory)nativeShellItem).GetImage(small, options, out hbitmap); + } + + Marshal.ReleaseComObject(nativeShellItem); + + return hr; + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Models/DimensionData.cs b/src/modules/peek/Peek.UI.WPF/Models/DimensionData.cs new file mode 100644 index 0000000000..42d0567238 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Models/DimensionData.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media.Imaging; + +namespace Peek.UI.Models +{ + public class DimensionData + { + public Size Size { get; set; } + + public Rotation Rotation { get; set; } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Models/IShellItem.cs b/src/modules/peek/Peek.UI.WPF/Models/IShellItem.cs new file mode 100644 index 0000000000..908e79428c --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Models/IShellItem.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using static Peek.UI.Native.NativeModels; + +namespace Peek.UI.Models +{ + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] + internal interface IShellItem + { + void BindToHandler( + IntPtr pbc, + [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, + [MarshalAs(UnmanagedType.LPStruct)] Guid riid, + out IntPtr ppv); + + void GetParent(out IShellItem ppsi); + + void GetDisplayName(Sigdn sigdnName, out IntPtr ppszName); + + void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); + + void Compare(IShellItem psi, uint hint, out int piOrder); + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Models/ObservableObject.cs b/src/modules/peek/Peek.UI.WPF/Models/ObservableObject.cs new file mode 100644 index 0000000000..8925029cbd --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Models/ObservableObject.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Peek.UI.Models +{ + public abstract class ObservableObject : INotifyPropertyChanged + { + public event PropertyChangedEventHandler? PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Models/ObservableRectangle.cs b/src/modules/peek/Peek.UI.WPF/Models/ObservableRectangle.cs new file mode 100644 index 0000000000..3a9c91c523 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Models/ObservableRectangle.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI.Models +{ + public class ObservableRectangle : ObservableObject + { + private double _left; + + public double Left + { + get + { + return _left; + } + + set + { + if (_left != value) + { + _left = value; + OnPropertyChanged(nameof(Left)); + } + } + } + + private double _top; + + public double Top + { + get + { + return _top; + } + + set + { + if (_top != value) + { + _top = value; + OnPropertyChanged(nameof(Top)); + } + } + } + + private double _height; + + public double Height + { + get + { + return _height; + } + + set + { + if (_height != value) + { + _height = value; + OnPropertyChanged(nameof(Height)); + } + } + } + + private double _width; + + public double Width + { + get + { + return _width; + } + + set + { + if (_width != value) + { + _width = value; + OnPropertyChanged(nameof(Width)); + } + } + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Models/ObservableWindowData.cs b/src/modules/peek/Peek.UI.WPF/Models/ObservableWindowData.cs new file mode 100644 index 0000000000..0aab543369 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Models/ObservableWindowData.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows; + +namespace Peek.UI.Models +{ + public class ObservableWindowData : ObservableObject + { + private double _titleBarHeight; + + public double TitleBarHeight + { + get + { + return _titleBarHeight; + } + + set + { + if (_titleBarHeight != value) + { + _titleBarHeight = value; + OnPropertyChanged(nameof(TitleBarHeight)); + } + } + } + + private ObservableRectangle _rectangle = new ObservableRectangle(); + + public ObservableRectangle Rectangle + { + get + { + return _rectangle; + } + + set + { + if (_rectangle != value) + { + _rectangle = value; + OnPropertyChanged(nameof(Rectangle)); + } + } + } + + private string _title = string.Empty; + + public string Title + { + get + { + return _title; + } + + set + { + if (_title != value) + { + _title = value; + OnPropertyChanged(nameof(Title)); + } + } + } + + private Visibility _visibility; + + public Visibility Visibility + { + get + { + return _visibility; + } + + set + { + if (_visibility != value) + { + _visibility = value; + + OnPropertyChanged(nameof(Visibility)); + } + } + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Native/NativeEventWaiter.cs b/src/modules/peek/Peek.UI.WPF/Native/NativeEventWaiter.cs new file mode 100644 index 0000000000..efb07bf413 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Native/NativeEventWaiter.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using System.Windows; + +namespace Peek.UI.Native +{ + public static class NativeEventWaiter + { + public static void WaitForEventLoop(string eventName, Action callback) + { + new Thread(() => + { + var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + while (true) + { + if (eventHandle.WaitOne()) + { + Application.Current.Dispatcher.Invoke(callback); + } + } + }).Start(); + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Native/NativeMethods.cs b/src/modules/peek/Peek.UI.WPF/Native/NativeMethods.cs new file mode 100644 index 0000000000..2be17a45d1 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Native/NativeMethods.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using Peek.UI.Models; +using static Peek.UI.Native.NativeModels; + +namespace Peek.UI.Native +{ + public static class NativeMethods + { + [DllImport("dwmapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern long DwmSetWindowAttribute( + IntPtr hwnd, + DwmWindowAttributed attribute, + ref DwmWindowCornerPreference pvAttribute, + uint cbAttribute); + + [DllImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool DeleteObject(IntPtr hObject); + + [DllImport("Shlwapi.dll", ExactSpelling = true, PreserveSig = false)] + internal static extern HResult AssocGetPerceivedType( + [MarshalAs(UnmanagedType.LPWStr)] string extension, + out PerceivedType perceivedType, + out Perceived perceivedFlags, + IntPtr ptrType); + + [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int SHCreateItemFromParsingName( + [MarshalAs(UnmanagedType.LPWStr)] string path, + IntPtr pbc, + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + internal static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); + + [DllImport("user32.dll")] + internal static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize); + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Native/NativeModels.cs b/src/modules/peek/Peek.UI.WPF/Native/NativeModels.cs new file mode 100644 index 0000000000..1cb3b6421e --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Native/NativeModels.cs @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace Peek.UI.Native +{ + public class NativeModels + { + public const int GwlExStyle = -20; + public const int WsExToolWindow = 0x00000080; + + public enum PerceivedType + { + Folder = -1, + Unknown = 0, + Image = 2, + Video = 4, + Document = 6, + } + + public enum Perceived + { + Undefined = 0x0000, + Softcoded = 0x0001, + Hardcoded = 0x0002, + NativeSupport = 0x0004, + GdiPlus = 0x0010, + WMSDK = 0x0020, + ZipFolder = 0x0040, + } + + public enum HResult + { + Ok = 0x0000, + False = 0x0001, + InvalidArguments = unchecked((int)0x80070057), + OutOfMemory = unchecked((int)0x8007000E), + NoInterface = unchecked((int)0x80004002), + Fail = unchecked((int)0x80004005), + ExtractionFailed = unchecked((int)0x8004B200), + ElementNotFound = unchecked((int)0x80070490), + TypeElementNotFound = unchecked((int)0x8002802B), + NoObject = unchecked((int)0x800401E5), + Win32ErrorCanceled = 1223, + Canceled = unchecked((int)0x800704C7), + ResourceInUse = unchecked((int)0x800700AA), + AccessDenied = unchecked((int)0x80030005), + } + + [StructLayout(LayoutKind.Sequential)] + public struct Input + { + public InputType Type; + public InputUnion Data; + + public static int Size + { + get { return Marshal.SizeOf(typeof(Input)); } + } + } + + [StructLayout(LayoutKind.Explicit)] + public struct InputUnion + { + [FieldOffset(0)] + public MouseInput Mi; + + [FieldOffset(0)] + public KeybdInput Ki; + + [FieldOffset(0)] + public HardwareInput Hi; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MouseInput + { + public int Dx; + public int Dy; + public int MouseData; + public uint DwFlags; + public uint Time; + public UIntPtr DwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct KeybdInput + { + public short WVk; + public short WScan; + public uint DwFlags; + public int Time; + public UIntPtr DwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct HardwareInput + { + public int UMsg; + public short WParamL; + public short WParamH; + } + + public enum InputType : uint + { + InputMouse = 0, + InputKeyboard = 1, + InputHardware = 2, + } + + public enum Sigdn : uint + { + NormalDisplay = 0, + ParentRelativeParsing = 0x80018001, + ParentRelativeForAddressBar = 0x8001c001, + DesktopAbsoluteParsing = 0x80028000, + ParentRelativeEditing = 0x80031001, + DesktopAbsoluteEditing = 0x8004c000, + FileSysPath = 0x80058000, + Url = 0x80068000, + } + + public enum DwmWindowAttributed + { + DwmaWindowCornerPreference = 33, + } + + // The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function + // what value of the enum to set. + public enum DwmWindowCornerPreference + { + DwmCpDefault = 0, + DwmCpDoNotRound = 1, + DwmCpRound = 2, + DwmCpRoundSmall = 3, + } + + [Flags] + public enum ThumbnailOptions + { + None = 0x00, + BiggerSizeOk = 0x01, + InMemoryOnly = 0x02, + IconOnly = 0x04, + ThumbnailOnly = 0x08, + InCacheOnly = 0x10, + ScaleUp = 0x100, + } + + [ComImport] + [Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IShellItemImageFactory + { + [PreserveSig] + HResult GetImage( + [In, MarshalAs(UnmanagedType.Struct)] NativeSize size, + [In] ThumbnailOptions flags, + [Out] out IntPtr phbm); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NativeSize + { + private int width; + private int height; + + public int Width + { + set { width = value; } + } + + public int Height + { + set { height = value; } + } + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Peek.UI.WPF.csproj b/src/modules/peek/Peek.UI.WPF/Peek.UI.WPF.csproj new file mode 100644 index 0000000000..42d2f14957 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Peek.UI.WPF.csproj @@ -0,0 +1,90 @@ + + + + WinExe + net7.0-windows10.0.19041.0 + PowerToys.Peek.UI + enable + true + false + false + ..\..\..\..\$(Platform)\$(Configuration)\modules\Peek\ + True + Resources\FluentIconsPeek.ico + + + + + tlbimp + 0 + 1 + 50a7e9b0-70ef-11d1-b75a-00a0c90564fe + 0 + false + true + + + tlbimp + 1 + 1 + eab22ac0-30c1-11cf-a7eb-0000c05bae0b + 0 + false + true + + + + + Resources\Peek.ico + + + + + + + + + Always + + + Always + + + + + + + + + + + + + + + + + $(DefaultXamlRuntime) + + + $(DefaultXamlRuntime) + + + $(DefaultXamlRuntime) + + + $(DefaultXamlRuntime) + + + $(DefaultXamlRuntime) + + + $(DefaultXamlRuntime) + + + + + + + + diff --git a/src/modules/peek/Peek.UI.WPF/Resources/Peek.ico b/src/modules/peek/Peek.UI.WPF/Resources/Peek.ico new file mode 100644 index 0000000000..daf81fdeb3 Binary files /dev/null and b/src/modules/peek/Peek.UI.WPF/Resources/Peek.ico differ diff --git a/src/modules/peek/Peek.UI.WPF/Themes/Dark.xaml b/src/modules/peek/Peek.UI.WPF/Themes/Dark.xaml new file mode 100644 index 0000000000..14204cfe7f --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Themes/Dark.xaml @@ -0,0 +1,19 @@ + + + + Dark.Accent1 + PowerToysPeek + Accent1 (Dark) + Dark + Accent1 + Black + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/Themes/HighContrast1.xaml b/src/modules/peek/Peek.UI.WPF/Themes/HighContrast1.xaml new file mode 100644 index 0000000000..cf168baaae --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Themes/HighContrast1.xaml @@ -0,0 +1,19 @@ + + + + HighContrast.Accent2 + PowerToysPeek + Accent2 (HighContrast) + HighContrast + Accent2 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/Themes/HighContrast2.xaml b/src/modules/peek/Peek.UI.WPF/Themes/HighContrast2.xaml new file mode 100644 index 0000000000..65d8065e29 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Themes/HighContrast2.xaml @@ -0,0 +1,19 @@ + + + + HighContrast.Accent3 + PowerToysPeek + Accent3 (HighContrast) + HighContrast + Accent3 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/Themes/HighContrastBlack.xaml b/src/modules/peek/Peek.UI.WPF/Themes/HighContrastBlack.xaml new file mode 100644 index 0000000000..402f414db6 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Themes/HighContrastBlack.xaml @@ -0,0 +1,19 @@ + + + + HighContrast.Accent4 + PowerToysPeek + Accent4 (HighContrast) + HighContrast + Accent4 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/Themes/HighContrastWhite.xaml b/src/modules/peek/Peek.UI.WPF/Themes/HighContrastWhite.xaml new file mode 100644 index 0000000000..bfe85de54f --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Themes/HighContrastWhite.xaml @@ -0,0 +1,19 @@ + + + + HighContrast.Accent5 + PowerToysPeek + Accent5 (HighContrast) + HighContrast + Accent5 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/Themes/Light.xaml b/src/modules/peek/Peek.UI.WPF/Themes/Light.xaml new file mode 100644 index 0000000000..4ca102d2fe --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Themes/Light.xaml @@ -0,0 +1,19 @@ + + + + Light.Accent1 + PowerToysPeek + Accent1 (Light) + Light + Accent1 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/ViewModels/MainViewModel.cs b/src/modules/peek/Peek.UI.WPF/ViewModels/MainViewModel.cs new file mode 100644 index 0000000000..406a36b4a3 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/ViewModels/MainViewModel.cs @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using Peek.UI.Extensions; +using Peek.UI.Helpers; +using Peek.UI.Models; +using Peek.UI.Native; +using WpfScreenHelper; +using Size = System.Windows.Size; + +namespace Peek.UI.ViewModels +{ + public class MainViewModel : ObservableObject, IDisposable + { + private const double ImageScale = 0.75; + private static readonly Size MinWindowSize = new Size(720, 720); + private static readonly Size AllowedContentGap = new Size(220, 220); + + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + + private CancellationToken CancellationToken => _cancellationTokenSource.Token; + + public IntPtr ForegroundWindowHandle { get; internal set; } + + public Image ImageControl { get; set; } + + public LinkedList SelectedFilePaths { get; set; } = new LinkedList(); + + private BitmapSource? _bitmap; + + public BitmapSource? Bitmap + { + get + { + return _bitmap; + } + + set + { + if (_bitmap != value) + { + _bitmap = value; + OnPropertyChanged(nameof(Bitmap)); + } + } + } + + private LinkedListNode? _currentSelectedFilePath; + + public LinkedListNode? CurrentSelectedFilePath + { + get + { + return _currentSelectedFilePath; + } + + set + { + if (_currentSelectedFilePath != value) + { + _currentSelectedFilePath = value; + var title = Path.GetFileName(_currentSelectedFilePath?.Value ?? string.Empty); + MainWindowData.Title = title; + OnPropertyChanged(nameof(CurrentSelectedFilePath)); + } + } + } + + public Visibility IsImageReady => IsLoading ? Visibility.Collapsed : Visibility.Visible; + + private bool _isLoading = true; + + public bool IsLoading + { + get + { + return _isLoading; + } + + set + { + if (_isLoading != value) + { + _isLoading = value; + OnPropertyChanged(nameof(IsLoading)); + OnPropertyChanged(nameof(IsImageReady)); + } + } + } + + private ObservableWindowData _mainWindowData = new ObservableWindowData(); + + public ObservableWindowData MainWindowData + { + get + { + return _mainWindowData; + } + + set + { + if (_mainWindowData != value) + { + _mainWindowData = value; + OnPropertyChanged(nameof(MainWindowData)); + } + } + } + + public MainViewModel(Image imageControl) + { + ImageControl = imageControl; + } + + // TODO: Implement proper disposal pattern + public void Dispose() + { + _cancellationTokenSource.Dispose(); + GC.SuppressFinalize(this); + } + + public void ClearSelection() + { + _cancellationTokenSource.Cancel(); + _cancellationTokenSource = new CancellationTokenSource(); + + CurrentSelectedFilePath = null; + MainWindowData.Visibility = Visibility.Collapsed; + } + + public bool TryUpdateSelectedFilePaths() + { + ForegroundWindowHandle = NativeMethods.GetForegroundWindow(); + + // TODO: Get all neighborings files in correct sorted order + var selectedItems = FileExplorerHelper.GetSelectedItems(ForegroundWindowHandle); + + var isDifferentSelectedItems = !SelectedFilePaths.SequenceEqual(selectedItems); + + if (isDifferentSelectedItems) + { + SelectedFilePaths = new LinkedList(selectedItems); + } + + CurrentSelectedFilePath = SelectedFilePaths.First; + + return isDifferentSelectedItems; + } + + // TODO: Implement proper cancellation pattern to support quick navigation + public async Task RenderImageToWindowAsync(string filename) + { + IsLoading = true; + + var screen = Screen.FromHandle(ForegroundWindowHandle); + Size maxWindowSize = new Size(screen.WpfBounds.Width * ImageScale, screen.WpfBounds.Height * ImageScale); + + // TODO: Support preview or thumbnail for document files + if (FileTypeHelper.IsSupportedImage(Path.GetExtension(filename))) + { + await RenderSupportedImageToWindowAsync(filename, screen.Bounds, maxWindowSize); + } + else if (FileTypeHelper.IsMedia(Path.GetExtension(filename)) || FileTypeHelper.IsDocument(Path.GetExtension(filename))) + { + await RenderMediaOrDocumentToWindowAsync(filename, screen.Bounds, maxWindowSize); + } + else + { + await RenderUnsupportedFileToWindowAsync(filename, screen.Bounds, maxWindowSize); + } + } + + private async Task RenderSupportedImageToWindowAsync(string filename, Rect windowBounds, Size maxWindowSize) + { + DimensionData dimensionData = await FileLoadHelper.LoadDimensionsAsync(filename); + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + var windowRect = dimensionData.Size.Fit(windowBounds, maxWindowSize, MinWindowSize, AllowedContentGap, MainWindowData.TitleBarHeight); + + MainWindowData.Rectangle.Width = windowRect.Width; + MainWindowData.Rectangle.Height = windowRect.Height; + MainWindowData.Rectangle.Left = windowRect.Left; + MainWindowData.Rectangle.Top = windowRect.Top; + + if (dimensionData.Size.Width > MainWindowData.Rectangle.Width || dimensionData.Size.Height > MainWindowData.Rectangle.Height) + { + ImageControl.StretchDirection = StretchDirection.Both; + } + else + { + ImageControl.StretchDirection = StretchDirection.DownOnly; + } + + await LoadImageAsync(filename, ImageControl, dimensionData.Rotation, CancellationToken); + } + + private async Task RenderMediaOrDocumentToWindowAsync(string filename, Rect windowBounds, Size maxWindowSize) + { + var bitmap = await FileLoadHelper.LoadThumbnailAsync(filename, true); + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + Bitmap = bitmap; + + var imageSize = new Size(bitmap.PixelWidth, bitmap.PixelHeight); + var windowRect = imageSize.Fit(windowBounds, maxWindowSize, MinWindowSize, AllowedContentGap, MainWindowData.TitleBarHeight); + + MainWindowData.Rectangle.Width = windowRect.Width; + MainWindowData.Rectangle.Height = windowRect.Height; + MainWindowData.Rectangle.Left = windowRect.Left; + MainWindowData.Rectangle.Top = windowRect.Top; + + MainWindowData.Visibility = Visibility.Visible; + IsLoading = false; + } + + private async Task RenderUnsupportedFileToWindowAsync(string filename, Rect windowBounds, Size maxWindowSize) + { + var contentSize = new Size(0, 0); + var windowRect = contentSize.Fit(windowBounds, maxWindowSize, MinWindowSize, AllowedContentGap, MainWindowData.TitleBarHeight); + + MainWindowData.Rectangle.Width = windowRect.Width; + MainWindowData.Rectangle.Height = windowRect.Height; + MainWindowData.Rectangle.Left = windowRect.Left; + MainWindowData.Rectangle.Top = windowRect.Top; + + var bitmap = await FileLoadHelper.LoadIconAsync(filename); + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + Bitmap = bitmap; + MainWindowData.Visibility = Visibility.Visible; + IsLoading = false; + } + + private Task LoadImageAsync(string filename, System.Windows.Controls.Image imageControl, Rotation rotation, CancellationToken cancellationToken) + { + bool isFullImageLoaded = false; + bool isThumbnailLoaded = false; + var thumbnailLoadTask = imageControl.Dispatcher.Invoke(async () => + { + var bitmap = await FileLoadHelper.LoadThumbnailAsync(filename, false); + isThumbnailLoaded = true; + + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + if (!isFullImageLoaded) + { + Bitmap = bitmap; + MainWindowData.Visibility = Visibility.Visible; + IsLoading = false; + } + }); + + var fullImageLoadTask = imageControl.Dispatcher.Invoke(async () => + { + var bitmap = await FileLoadHelper.LoadFullImageAsync(filename, rotation); + isFullImageLoaded = true; + + if (CancellationToken.IsCancellationRequested) + { + _cancellationTokenSource = new CancellationTokenSource(); + return; + } + + Bitmap = bitmap; + if (!isThumbnailLoaded) + { + MainWindowData.Visibility = Visibility.Visible; + IsLoading = false; + } + }); + + return Task.WhenAll(thumbnailLoadTask, fullImageLoadTask); + } + } +} diff --git a/src/modules/peek/Peek.UI.WPF/Views/MainWindow.xaml b/src/modules/peek/Peek.UI.WPF/Views/MainWindow.xaml new file mode 100644 index 0000000000..6da3d32725 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Views/MainWindow.xaml @@ -0,0 +1,37 @@ + + + + + + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI.WPF/Views/MainWindow.xaml.cs b/src/modules/peek/Peek.UI.WPF/Views/MainWindow.xaml.cs new file mode 100644 index 0000000000..dcfbc5cd53 --- /dev/null +++ b/src/modules/peek/Peek.UI.WPF/Views/MainWindow.xaml.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Windows; +using System.Windows.Input; +using interop; +using ModernWpf.Controls; +using Peek.UI.Extensions; +using Peek.UI.Native; +using Peek.UI.ViewModels; + +namespace Peek.UI.Views +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window, IDisposable + { + private readonly MainViewModel _viewModel; + + public MainWindow() + { + InitializeComponent(); + + this.RoundCorners(); + + _viewModel = new MainViewModel(ImageControl); + _viewModel.PropertyChanged += MainViewModel_PropertyChanged; + + DataContext = _viewModel; + + NativeEventWaiter.WaitForEventLoop(Constants.ShowPeekEvent(), OnPeekHotkey); + + Loaded += MainWindow_Loaded; + Closing += MainWindow_Closing; + KeyDown += MainWindow_KeyDown; + } + + private void MainWindow_Loaded(object sender, RoutedEventArgs e) + { + _viewModel.MainWindowData.Visibility = Visibility.Collapsed; + _viewModel.MainWindowData.TitleBarHeight = TitleBar.GetHeight(this); + _viewModel.ImageControl = ImageControl; + } + + private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e) + { + _viewModel.MainWindowData.Visibility = Visibility.Collapsed; + e.Cancel = true; + } + + private void MainWindow_KeyDown(object? sender, KeyEventArgs e) + { + if (!e.IsRepeat && _viewModel.CurrentSelectedFilePath != null) + { + switch (e.Key) + { + case Key.Left: + _viewModel.CurrentSelectedFilePath = _viewModel.CurrentSelectedFilePath.GetPreviousOrLast(); + e.Handled = true; + break; + + case Key.Right: + _viewModel.CurrentSelectedFilePath = _viewModel.CurrentSelectedFilePath.GetNextOrFirst(); + e.Handled = true; + break; + + default: break; + } + } + } + + public void Dispose() + { + _viewModel.Dispose(); + GC.SuppressFinalize(this); + } + + protected override void OnSourceInitialized(EventArgs e) + { + base.OnSourceInitialized(e); + this.SetToolStyle(); + } + + private async void MainViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(MainViewModel.CurrentSelectedFilePath): + if (_viewModel.CurrentSelectedFilePath != null) + { + await _viewModel.RenderImageToWindowAsync(_viewModel.CurrentSelectedFilePath.Value); + } + + break; + } + } + + private void OnPeekHotkey() + { + if (IsActive && _viewModel.MainWindowData.Visibility == Visibility.Visible) + { + _viewModel.ClearSelection(); + } + else + { + _viewModel.TryUpdateSelectedFilePaths(); + } + + this.BringToForeground(); + } + } +} diff --git a/src/modules/peek/Peek.UI/App.xaml b/src/modules/peek/Peek.UI/App.xaml new file mode 100644 index 0000000000..7c98d9b1c1 --- /dev/null +++ b/src/modules/peek/Peek.UI/App.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.UI/App.xaml.cs b/src/modules/peek/Peek.UI/App.xaml.cs new file mode 100644 index 0000000000..431df1bb7d --- /dev/null +++ b/src/modules/peek/Peek.UI/App.xaml.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI +{ + using System; + using System.Diagnostics; + using System.Threading; + using ManagedCommon; + using Microsoft.UI.Dispatching; + using Microsoft.UI.Xaml; + using WinUIEx; + + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : Application + { + public static int PowerToysPID { get; set; } + + /// + /// Initializes a new instance of the class. + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } + + /// + /// Invoked when the application is launched. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var cmdArgs = Environment.GetCommandLineArgs(); + if (cmdArgs?.Length > 1) + { + if (int.TryParse(cmdArgs[cmdArgs.Length - 1], out int powerToysRunnerPid)) + { + RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () => + { + Environment.Exit(0); + }); + } + } + + window = new MainWindow(); + + window.Activate(); + window.Hide(); + } + + private Window? window; + } +} diff --git a/src/modules/peek/Peek.UI/Assets/LockScreenLogo.scale-200.png b/src/modules/peek/Peek.UI/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000000..7440f0d4bf Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/LockScreenLogo.scale-200.png differ diff --git a/src/modules/peek/Peek.UI/Assets/Peek.ico b/src/modules/peek/Peek.UI/Assets/Peek.ico new file mode 100644 index 0000000000..daf81fdeb3 Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/Peek.ico differ diff --git a/src/modules/peek/Peek.UI/Assets/SplashScreen.scale-200.png b/src/modules/peek/Peek.UI/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000000..32f486a867 Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/SplashScreen.scale-200.png differ diff --git a/src/modules/peek/Peek.UI/Assets/Square150x150Logo.scale-200.png b/src/modules/peek/Peek.UI/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000000..53ee3777ea Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/Square150x150Logo.scale-200.png differ diff --git a/src/modules/peek/Peek.UI/Assets/Square44x44Logo.scale-200.png b/src/modules/peek/Peek.UI/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000000..f713bba67f Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/Square44x44Logo.scale-200.png differ diff --git a/src/modules/peek/Peek.UI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/modules/peek/Peek.UI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000000..dc9f5bea0c Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/src/modules/peek/Peek.UI/Assets/StoreLogo.png b/src/modules/peek/Peek.UI/Assets/StoreLogo.png new file mode 100644 index 0000000000..a4586f26bd Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/StoreLogo.png differ diff --git a/src/modules/peek/Peek.UI/Assets/Wide310x150Logo.scale-200.png b/src/modules/peek/Peek.UI/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000000..8b4a5d0dd5 Binary files /dev/null and b/src/modules/peek/Peek.UI/Assets/Wide310x150Logo.scale-200.png differ diff --git a/src/modules/peek/Peek.UI/Extensions/SizeExtensions.cs b/src/modules/peek/Peek.UI/Extensions/SizeExtensions.cs new file mode 100644 index 0000000000..fda66f05b9 --- /dev/null +++ b/src/modules/peek/Peek.UI/Extensions/SizeExtensions.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI.Extensions +{ + using Windows.Foundation; + + public static class SizeExtensions + { + public static Size Fit(this Size sizeToFit, Size maxSize, Size minSize) + { + double fittedWidth = sizeToFit.Width; + double fittedHeight = sizeToFit.Height; + + double ratioWidth = sizeToFit.Width / maxSize.Width; + double ratioHeight = sizeToFit.Height / maxSize.Height; + + if (ratioWidth > ratioHeight) + { + if (ratioWidth > 1) + { + fittedWidth = maxSize.Width; + fittedHeight = sizeToFit.Height / ratioWidth; + } + } + else + { + if (ratioHeight > 1) + { + fittedWidth = sizeToFit.Width / ratioHeight; + fittedHeight = maxSize.Height; + } + } + + if (fittedWidth < minSize.Width) + { + fittedWidth = minSize.Width; + } + + if (fittedHeight < minSize.Height) + { + fittedHeight = minSize.Height; + } + + return new Size(fittedWidth, fittedHeight); + } + } +} diff --git a/src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs b/src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs new file mode 100644 index 0000000000..858e449676 --- /dev/null +++ b/src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI.Extensions +{ + using System.Drawing; + using Microsoft.UI.Xaml; + using Windows.Win32; + using Windows.Win32.Foundation; + using Windows.Win32.Graphics.Gdi; + using WinUIEx; + + public static class WindowExtensions + { + public static Size GetMonitorSize(this Window window) + { + var hwnd = new HWND(window.GetWindowHandle()); + var hwndDesktop = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + MONITORINFO info = new (); + info.cbSize = 40; + PInvoke.GetMonitorInfo(hwndDesktop, ref info); + var monitorWidth = info.rcMonitor.left + info.rcMonitor.right; + var monitorHeight = info.rcMonitor.bottom + info.rcMonitor.top; + + return new Size(monitorWidth, monitorHeight); + } + } +} diff --git a/src/modules/peek/Peek.UI/Helpers/FileExplorerHelper.cs b/src/modules/peek/Peek.UI/Helpers/FileExplorerHelper.cs new file mode 100644 index 0000000000..79975307fc --- /dev/null +++ b/src/modules/peek/Peek.UI/Helpers/FileExplorerHelper.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Peek.Common.Models; +using Peek.UI.Native; + +namespace Peek.UI.Helpers +{ + public static class FileExplorerHelper + { + public static List GetSelectedFileExplorerFiles() + { + var foregroundWindowHandle = NativeMethods.GetForegroundWindow(); + + var selectedItems = new List(); + var shell = new Shell32.Shell(); + foreach (SHDocVw.InternetExplorer window in shell.Windows()) + { + if (window.HWND == (int)foregroundWindowHandle) + { + Shell32.FolderItems items = ((Shell32.IShellFolderViewDual2)window.Document).SelectedItems(); + if (items != null && items.Count > 0) + { + foreach (Shell32.FolderItem item in items) + { + selectedItems.Add(new File(item.Path)); + } + } + } + } + + return selectedItems; + } + } +} diff --git a/src/modules/peek/Peek.UI/MainWindow.xaml b/src/modules/peek/Peek.UI/MainWindow.xaml new file mode 100644 index 0000000000..c261512c79 --- /dev/null +++ b/src/modules/peek/Peek.UI/MainWindow.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.UI/MainWindow.xaml.cs b/src/modules/peek/Peek.UI/MainWindow.xaml.cs new file mode 100644 index 0000000000..0ffc4af95c --- /dev/null +++ b/src/modules/peek/Peek.UI/MainWindow.xaml.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI +{ + using System.Collections.Generic; + using System.Linq; + using interop; + using Microsoft.UI.Windowing; + using Peek.Common.Models; + using Peek.FilePreviewer.Models; + using Peek.UI.Extensions; + using Peek.UI.Helpers; + using Peek.UI.Native; + using Windows.Foundation; + using WinUIEx; + + /// + /// An empty window that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MainWindow : WindowEx + { + public MainWindow() + { + InitializeComponent(); + + ViewModel = new MainWindowViewModel(); + + NativeEventWaiter.WaitForEventLoop(Constants.ShowPeekEvent(), OnPeekHotkey); + + TitleBarControl.SetToWindow(this); + + AppWindow.Closing += AppWindow_Closing; + } + + public MainWindowViewModel ViewModel { get; } + + /// + /// Handle Peek hotkey, by toggling the window visibility and querying files when necessary. + /// + private void OnPeekHotkey() + { + if (AppWindow.IsVisible) + { + this.Hide(); + ViewModel.Files = new List(); + ViewModel.CurrentFile = null; + } + else + { + var fileExplorerSelectedFiles = FileExplorerHelper.GetSelectedFileExplorerFiles(); + if (fileExplorerSelectedFiles.Count == 0) + { + return; + } + + ViewModel.Files = fileExplorerSelectedFiles; + ViewModel.CurrentFile = fileExplorerSelectedFiles.First(); + } + } + + /// + /// Handle FilePreviewerSizeChanged event to adjust window size and position accordingly. + /// + /// object + /// PreviewSizeChangedArgs + private void FilePreviewer_PreviewSizeChanged(object sender, PreviewSizeChangedArgs e) + { + var requestedSize = e.WindowSizeRequested; + var monitorSize = this.GetMonitorSize(); + + // TODO: Use design-defined rules for adjusted window size + var titleBarHeight = TitleBarControl.ActualHeight; + var maxContentSize = new Size(monitorSize.Width * 0.8, (monitorSize.Height - titleBarHeight) * 0.8); + var minContentSize = new Size(500, 500 - titleBarHeight); + + var adjustedContentSize = requestedSize.Fit(maxContentSize, minContentSize); + + // TODO: Only re-center if window has not been resized by user (or use design-defined logic). + // TODO: Investigate why portrait images do not perfectly fit edge-to-edge + this.CenterOnScreen(adjustedContentSize.Width, adjustedContentSize.Height + titleBarHeight); + this.Show(); + this.BringToFront(); + } + + /// + /// Handle AppWindow closing to prevent app termination on close. + /// + /// AppWindow + /// AppWindowClosingEventArgs + private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args) + { + args.Cancel = true; + this.Hide(); + } + } +} diff --git a/src/modules/peek/Peek.UI/MainWindowViewModel.cs b/src/modules/peek/Peek.UI/MainWindowViewModel.cs new file mode 100644 index 0000000000..0a923eda31 --- /dev/null +++ b/src/modules/peek/Peek.UI/MainWindowViewModel.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI +{ + using System.Collections.Generic; + using CommunityToolkit.Mvvm.ComponentModel; + using Peek.Common.Models; + + public partial class MainWindowViewModel : ObservableObject + { + [ObservableProperty] + private File? currentFile; + + [ObservableProperty] + private List files = new (); + } +} diff --git a/src/modules/peek/Peek.UI/Native/NativeMethods.cs b/src/modules/peek/Peek.UI/Native/NativeMethods.cs new file mode 100644 index 0000000000..5335ac05c3 --- /dev/null +++ b/src/modules/peek/Peek.UI/Native/NativeMethods.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI.Native +{ + using System; + using System.Runtime.InteropServices; + + public static class NativeMethods + { + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + internal static extern IntPtr GetForegroundWindow(); + } +} diff --git a/src/modules/peek/Peek.UI/NativeEventWaiter.cs b/src/modules/peek/Peek.UI/NativeEventWaiter.cs new file mode 100644 index 0000000000..7726bede0c --- /dev/null +++ b/src/modules/peek/Peek.UI/NativeEventWaiter.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using Microsoft.UI.Dispatching; + +namespace Peek.UI.Native +{ + public static class NativeEventWaiter + { + public static void WaitForEventLoop(string eventName, Action callback) + { + var dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + new Thread(() => + { + var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + while (true) + { + if (eventHandle.WaitOne()) + { + dispatcherQueue.TryEnqueue(() => callback()); + } + } + }).Start(); + } + } +} diff --git a/src/modules/peek/Peek.UI/NativeMethods.txt b/src/modules/peek/Peek.UI/NativeMethods.txt new file mode 100644 index 0000000000..bdccc2f7ba --- /dev/null +++ b/src/modules/peek/Peek.UI/NativeMethods.txt @@ -0,0 +1,2 @@ +MonitorFromWindow +GetMonitorInfo \ No newline at end of file diff --git a/src/modules/peek/Peek.UI/Package.appxmanifest b/src/modules/peek/Peek.UI/Package.appxmanifest new file mode 100644 index 0000000000..9116adec94 --- /dev/null +++ b/src/modules/peek/Peek.UI/Package.appxmanifest @@ -0,0 +1,48 @@ + + + + + + + + Peek.UI + sachaple + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.UI/Peek.UI.csproj b/src/modules/peek/Peek.UI/Peek.UI.csproj new file mode 100644 index 0000000000..6803b6e51e --- /dev/null +++ b/src/modules/peek/Peek.UI/Peek.UI.csproj @@ -0,0 +1,97 @@ + + + PowerToys.Peek.UI + PowerToys.Peek.UI + PowerToys Peek UI + Peek.UI + WinExe + net7.0-windows10.0.19041.0 + 10.0.19041.0 + ..\..\..\..\$(Platform)\$(Configuration)\modules\Peek\ + app.manifest + x86;x64;ARM64 + win10-x86;win10-x64;win10-arm64 + true + true + true + false + false + true + None + true + 10.0.19041.0 + Enable + + + + + + + + + + + 0 + 1 + 50a7e9b0-70ef-11d1-b75a-00a0c90564fe + 0 + tlbimp + false + true + + + 1 + 1 + eab22ac0-30c1-11cf-a7eb-0000c05bae0b + 0 + tlbimp + false + true + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + + + diff --git a/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw new file mode 100644 index 0000000000..5fb536c8bb --- /dev/null +++ b/src/modules/peek/Peek.UI/Strings/en-us/Resources.resw @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Peek + + + Peek + + \ No newline at end of file diff --git a/src/modules/peek/Peek.UI/Views/TitleBar.xaml b/src/modules/peek/Peek.UI/Views/TitleBar.xaml new file mode 100644 index 0000000000..eb1da3b5e8 --- /dev/null +++ b/src/modules/peek/Peek.UI/Views/TitleBar.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/src/modules/peek/Peek.UI/Views/TitleBar.xaml.cs b/src/modules/peek/Peek.UI/Views/TitleBar.xaml.cs new file mode 100644 index 0000000000..fac1f78bf4 --- /dev/null +++ b/src/modules/peek/Peek.UI/Views/TitleBar.xaml.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Peek.UI.Views +{ + using System; + using ManagedCommon; + using Microsoft.UI; + using Microsoft.UI.Windowing; + using Microsoft.UI.Xaml.Controls; + using WinUIEx; + using MUX = Microsoft.UI.Xaml; + + public sealed partial class TitleBar : UserControl + { + public TitleBar() + { + InitializeComponent(); + } + + public void SetToWindow(MainWindow mainWindow) + { + if (AppWindowTitleBar.IsCustomizationSupported()) + { + AppWindow window = mainWindow.GetAppWindow(); + window.TitleBar.ExtendsContentIntoTitleBar = true; + window.TitleBar.ButtonBackgroundColor = Colors.Transparent; + mainWindow.SetTitleBar(this); + } + else + { + var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this); + ThemeHelpers.SetImmersiveDarkMode(hWnd, ThemeHelpers.GetAppTheme() == AppTheme.Dark); + Visibility = MUX.Visibility.Collapsed; + + // Set window icon + WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd); + AppWindow appWindow = AppWindow.GetFromWindowId(windowId); + appWindow.SetIcon("Assets/Peek.ico"); + } + } + } +} diff --git a/src/modules/peek/Peek.UI/app.manifest b/src/modules/peek/Peek.UI/app.manifest new file mode 100644 index 0000000000..c1deabb494 --- /dev/null +++ b/src/modules/peek/Peek.UI/app.manifest @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + \ No newline at end of file diff --git a/src/modules/peek/WIC/Classes/CLSID.cs b/src/modules/peek/WIC/Classes/CLSID.cs new file mode 100644 index 0000000000..63bc6f575d --- /dev/null +++ b/src/modules/peek/WIC/Classes/CLSID.cs @@ -0,0 +1,7 @@ +namespace WIC +{ + internal static class CLSID + { + public const string WICImagingFactory = "cacaf262-9370-4615-a13b-9f5539da4c0a"; + } +} diff --git a/src/modules/peek/WIC/Classes/WICImagingFactory.cs b/src/modules/peek/WIC/Classes/WICImagingFactory.cs new file mode 100644 index 0000000000..7792311c50 --- /dev/null +++ b/src/modules/peek/WIC/Classes/WICImagingFactory.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICImagingFactory)] + [CoClass(typeof(WICImagingFactoryClass))] + public interface WICImagingFactory : IWICImagingFactory { } + + [ComImport] + [Guid(CLSID.WICImagingFactory)] + [ComDefaultInterface(typeof(IWICImagingFactory))] + public class WICImagingFactoryClass { } +} diff --git a/src/modules/peek/WIC/CoTaskMemPtr.cs b/src/modules/peek/WIC/CoTaskMemPtr.cs new file mode 100644 index 0000000000..c9029f3331 --- /dev/null +++ b/src/modules/peek/WIC/CoTaskMemPtr.cs @@ -0,0 +1,43 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + internal struct CoTaskMemPtr : IDisposable + { + public static CoTaskMemPtr From(T? nullableStructure) where T : struct + { + IntPtr value; + if (nullableStructure.HasValue) + { + value = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(T))); + Marshal.StructureToPtr(nullableStructure, value, false); + } + else + { + value = IntPtr.Zero; + } + return new CoTaskMemPtr(value); + } + + public CoTaskMemPtr(IntPtr value) + { + this.value = value; + } + + private IntPtr value; + + public static implicit operator IntPtr(CoTaskMemPtr safeIntPtr) + { + return safeIntPtr.value; + } + + public void Dispose() + { + if (value != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(value); + } + } + } +} diff --git a/src/modules/peek/WIC/Constants/ContainerFormat.cs b/src/modules/peek/WIC/Constants/ContainerFormat.cs new file mode 100644 index 0000000000..e4b5b8da8b --- /dev/null +++ b/src/modules/peek/WIC/Constants/ContainerFormat.cs @@ -0,0 +1,16 @@ +using System; + +namespace WIC +{ + public static class ContainerFormat + { + public static readonly Guid Bmp = new Guid(0x0af1d87e, 0xfcfe, 0x4188, 0xbd, 0xeb, 0xa7, 0x90, 0x64, 0x71, 0xcb, 0xe3); + public static readonly Guid Png = new Guid(0x1b7cfaf4, 0x713f, 0x473c, 0xbb, 0xcd, 0x61, 0x37, 0x42, 0x5f, 0xae, 0xaf); + public static readonly Guid Ico = new Guid(0xa3a860c4, 0x338f, 0x4c17, 0x91, 0x9a, 0xfb, 0xa4, 0xb5, 0x62, 0x8f, 0x21); + public static readonly Guid Jpeg = new Guid(0x19e4a5aa, 0x5662, 0x4fc5, 0xa0, 0xc0, 0x17, 0x58, 0x02, 0x8e, 0x10, 0x57); + public static readonly Guid Tiff = new Guid(0x163bcc30, 0xe2e9, 0x4f0b, 0x96, 0x1d, 0xa3, 0xe9, 0xfd, 0xb7, 0x88, 0xa3); + public static readonly Guid Gif = new Guid(0x1f8a5601, 0x7d4d, 0x4cbd, 0x9c, 0x82, 0x1b, 0xc8, 0xd4, 0xee, 0xb9, 0xa5); + public static readonly Guid Wmp = new Guid(0x57a37caa, 0x367a, 0x4540, 0x91, 0x6b, 0xf1, 0x83, 0xc5, 0x09, 0x3a, 0x4b); + public static readonly Guid Dds = new Guid(0x9967cb95, 0x2e85, 0x4ac8, 0x8c, 0xa2, 0x83, 0xd7, 0xcc, 0xd4, 0x25, 0xc9); + } +} diff --git a/src/modules/peek/WIC/Constants/HResult.cs b/src/modules/peek/WIC/Constants/HResult.cs new file mode 100644 index 0000000000..341c8530d2 --- /dev/null +++ b/src/modules/peek/WIC/Constants/HResult.cs @@ -0,0 +1,7 @@ +namespace WIC +{ + public struct HResult + { + public const int WINCODEC_ERR_PROPERTYNOTFOUND = unchecked((int)0x88982F40); + } +} diff --git a/src/modules/peek/WIC/Constants/Vendor.cs b/src/modules/peek/WIC/Constants/Vendor.cs new file mode 100644 index 0000000000..6377f0b735 --- /dev/null +++ b/src/modules/peek/WIC/Constants/Vendor.cs @@ -0,0 +1,10 @@ +using System; + +namespace WIC +{ + public static class Vendor + { + public static readonly Guid Microsoft = new Guid(0xf0e749ca, 0xedef, 0x4589, 0xa7, 0x3a, 0xee, 0xe, 0x62, 0x6a, 0x2a, 0x2b); + public static readonly Guid MicrosoftBuiltIn = new Guid(0x257a30fd, 0x6b6, 0x462b, 0xae, 0xa4, 0x63, 0xf7, 0xb, 0x86, 0xe5, 0x33); + } +} diff --git a/src/modules/peek/WIC/Constants/WICPixelFormat.cs b/src/modules/peek/WIC/Constants/WICPixelFormat.cs new file mode 100644 index 0000000000..1e6701ec44 --- /dev/null +++ b/src/modules/peek/WIC/Constants/WICPixelFormat.cs @@ -0,0 +1,98 @@ +using System; + +namespace WIC.Constants +{ + public static class WICPixelFormat + { + public static readonly Guid WICPixelFormatDontCare = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x00); + public static readonly Guid WICPixelFormat1bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x01); + public static readonly Guid WICPixelFormat2bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x02); + public static readonly Guid WICPixelFormat4bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x03); + public static readonly Guid WICPixelFormat8bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x04); + public static readonly Guid WICPixelFormatBlackWhite = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x05); + public static readonly Guid WICPixelFormat2bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x06); + public static readonly Guid WICPixelFormat4bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x07); + public static readonly Guid WICPixelFormat8bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x08); + public static readonly Guid WICPixelFormat8bppAlpha = new Guid(0xe6cd0116, 0xeeba, 0x4161, 0xaa, 0x85, 0x27, 0xdd, 0x9f, 0xb3, 0xa8, 0x95); + public static readonly Guid WICPixelFormat16bppBGR555 = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x09); + public static readonly Guid WICPixelFormat16bppBGR565 = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0a); + public static readonly Guid WICPixelFormat16bppBGRA5551 = new Guid(0x05ec7c2b, 0xf1e6, 0x4961, 0xad, 0x46, 0xe1, 0xcc, 0x81, 0x0a, 0x87, 0xd2); + public static readonly Guid WICPixelFormat16bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0b); + public static readonly Guid WICPixelFormat24bppBGR = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c); + public static readonly Guid WICPixelFormat24bppRGB = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d); + public static readonly Guid WICPixelFormat32bppBGR = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0e); + public static readonly Guid WICPixelFormat32bppBGRA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f); + public static readonly Guid WICPixelFormat32bppPBGRA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x10); + public static readonly Guid WICPixelFormat32bppGrayFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x11); + public static readonly Guid WICPixelFormat32bppRGB = new Guid(0xd98c6b95, 0x3efe, 0x47d6, 0xbb, 0x25, 0xeb, 0x17, 0x48, 0xab, 0x0c, 0xf1); + public static readonly Guid WICPixelFormat32bppRGBA = new Guid(0xf5c7ad2d, 0x6a8d, 0x43dd, 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9); + public static readonly Guid WICPixelFormat32bppPRGBA = new Guid(0x3cc4a650, 0xa527, 0x4d37, 0xa9, 0x16, 0x31, 0x42, 0xc7, 0xeb, 0xed, 0xba); + public static readonly Guid WICPixelFormat48bppRGB = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x15); + public static readonly Guid WICPixelFormat48bppBGR = new Guid(0xe605a384, 0xb468, 0x46ce, 0xbb, 0x2e, 0x36, 0xf1, 0x80, 0xe6, 0x43, 0x13); + public static readonly Guid WICPixelFormat64bppRGB = new Guid(0xa1182111, 0x186d, 0x4d42, 0xbc, 0x6a, 0x9c, 0x83, 0x03, 0xa8, 0xdf, 0xf9); + public static readonly Guid WICPixelFormat64bppRGBA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16); + public static readonly Guid WICPixelFormat64bppBGRA = new Guid(0x1562ff7c, 0xd352, 0x46f9, 0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46); + public static readonly Guid WICPixelFormat64bppPRGBA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x17); + public static readonly Guid WICPixelFormat64bppPBGRA = new Guid(0x8c518e8e, 0xa4ec, 0x468b, 0xae, 0x70, 0xc9, 0xa3, 0x5a, 0x9c, 0x55, 0x30); + public static readonly Guid WICPixelFormat16bppGrayFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x13); + public static readonly Guid WICPixelFormat32bppBGR101010 = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x14); + public static readonly Guid WICPixelFormat48bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x12); + public static readonly Guid WICPixelFormat48bppBGRFixedPoint = new Guid(0x49ca140e, 0xcab6, 0x493b, 0x9d, 0xdf, 0x60, 0x18, 0x7c, 0x37, 0x53, 0x2a); + public static readonly Guid WICPixelFormat96bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x18); + public static readonly Guid WICPixelFormat96bppRGBFloat = new Guid(0xe3fed78f, 0xe8db, 0x4acf, 0x84, 0xc1, 0xe9, 0x7f, 0x61, 0x36, 0xb3, 0x27); + public static readonly Guid WICPixelFormat128bppRGBAFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x19); + public static readonly Guid WICPixelFormat128bppPRGBAFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1a); + public static readonly Guid WICPixelFormat128bppRGBFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1b); + public static readonly Guid WICPixelFormat32bppCMYK = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1c); + public static readonly Guid WICPixelFormat64bppRGBAFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1d); + public static readonly Guid WICPixelFormat64bppBGRAFixedPoint = new Guid(0x356de33c, 0x54d2, 0x4a23, 0xbb, 0x4, 0x9b, 0x7b, 0xf9, 0xb1, 0xd4, 0x2d); + public static readonly Guid WICPixelFormat64bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x40); + public static readonly Guid WICPixelFormat128bppRGBAFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1e); + public static readonly Guid WICPixelFormat128bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x41); + public static readonly Guid WICPixelFormat64bppRGBAHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3a); + public static readonly Guid WICPixelFormat64bppPRGBAHalf = new Guid(0x58ad26c2, 0xc623, 0x4d9d, 0xb3, 0x20, 0x38, 0x7e, 0x49, 0xf8, 0xc4, 0x42); + public static readonly Guid WICPixelFormat64bppRGBHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x42); + public static readonly Guid WICPixelFormat48bppRGBHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3b); + public static readonly Guid WICPixelFormat32bppRGBE = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3d); + public static readonly Guid WICPixelFormat16bppGrayHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3e); + public static readonly Guid WICPixelFormat32bppGrayFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3f); + public static readonly Guid WICPixelFormat32bppRGBA1010102 = new Guid(0x25238D72, 0xFCF9, 0x4522, 0xb5, 0x14, 0x55, 0x78, 0xe5, 0xad, 0x55, 0xe0); + public static readonly Guid WICPixelFormat32bppRGBA1010102XR = new Guid(0x00DE6B9A, 0xC101, 0x434b, 0xb5, 0x02, 0xd0, 0x16, 0x5e, 0xe1, 0x12, 0x2c); + public static readonly Guid WICPixelFormat32bppR10G10B10A2 = new Guid(0x604e1bb5, 0x8a3c, 0x4b65, 0xb1, 0x1c, 0xbc, 0x0b, 0x8d, 0xd7, 0x5b, 0x7f); + public static readonly Guid WICPixelFormat32bppR10G10B10A2HDR10 = new Guid(0x9c215c5d, 0x1acc, 0x4f0e, 0xa4, 0xbc, 0x70, 0xfb, 0x3a, 0xe8, 0xfd, 0x28); + public static readonly Guid WICPixelFormat64bppCMYK = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1f); + public static readonly Guid WICPixelFormat24bpp3Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x20); + public static readonly Guid WICPixelFormat32bpp4Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x21); + public static readonly Guid WICPixelFormat40bpp5Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x22); + public static readonly Guid WICPixelFormat48bpp6Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x23); + public static readonly Guid WICPixelFormat56bpp7Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x24); + public static readonly Guid WICPixelFormat64bpp8Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x25); + public static readonly Guid WICPixelFormat48bpp3Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x26); + public static readonly Guid WICPixelFormat64bpp4Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x27); + public static readonly Guid WICPixelFormat80bpp5Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x28); + public static readonly Guid WICPixelFormat96bpp6Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x29); + public static readonly Guid WICPixelFormat112bpp7Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2a); + public static readonly Guid WICPixelFormat128bpp8Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2b); + public static readonly Guid WICPixelFormat40bppCMYKAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2c); + public static readonly Guid WICPixelFormat80bppCMYKAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2d); + public static readonly Guid WICPixelFormat32bpp3ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2e); + public static readonly Guid WICPixelFormat40bpp4ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2f); + public static readonly Guid WICPixelFormat48bpp5ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x30); + public static readonly Guid WICPixelFormat56bpp6ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x31); + public static readonly Guid WICPixelFormat64bpp7ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x32); + public static readonly Guid WICPixelFormat72bpp8ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x33); + public static readonly Guid WICPixelFormat64bpp3ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x34); + public static readonly Guid WICPixelFormat80bpp4ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x35); + public static readonly Guid WICPixelFormat96bpp5ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x36); + public static readonly Guid WICPixelFormat112bpp6ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x37); + public static readonly Guid WICPixelFormat128bpp7ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x38); + public static readonly Guid WICPixelFormat144bpp8ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x39); + public static readonly Guid WICPixelFormat8bppY = new Guid(0x91B4DB54, 0x2DF9, 0x42F0, 0xB4, 0x49, 0x29, 0x09, 0xBB, 0x3D, 0xF8, 0x8E); + public static readonly Guid WICPixelFormat8bppCb = new Guid(0x1339F224, 0x6BFE, 0x4C3E, 0x93, 0x02, 0xE4, 0xF3, 0xA6, 0xD0, 0xCA, 0x2A); + public static readonly Guid WICPixelFormat8bppCr = new Guid(0xB8145053, 0x2116, 0x49F0, 0x88, 0x35, 0xED, 0x84, 0x4B, 0x20, 0x5C, 0x51); + public static readonly Guid WICPixelFormat16bppCbCr = new Guid(0xFF95BA6E, 0x11E0, 0x4263, 0xBB, 0x45, 0x01, 0x72, 0x1F, 0x34, 0x60, 0xA4); + public static readonly Guid WICPixelFormat16bppYQuantizedDctCoefficients = new Guid(0xA355F433, 0x48E8, 0x4A42, 0x84, 0xD8, 0xE2, 0xAA, 0x26, 0xCA, 0x80, 0xA4); + public static readonly Guid WICPixelFormat16bppCbQuantizedDctCoefficients = new Guid(0xD2C4FF61, 0x56A5, 0x49C2, 0x8B, 0x5C, 0x4C, 0x19, 0x25, 0x96, 0x48, 0x37); + public static readonly Guid WICPixelFormat16bppCrQuantizedDctCoefficients = new Guid(0x2FE354F0, 0x1680, 0x42D8, 0x92, 0x31, 0xE7, 0x3C, 0x05, 0x65, 0xBF, 0xC1); + } +} diff --git a/src/modules/peek/WIC/Enumerations/ExifColorSpace.cs b/src/modules/peek/WIC/Enumerations/ExifColorSpace.cs new file mode 100644 index 0000000000..ebf6047cbb --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/ExifColorSpace.cs @@ -0,0 +1,8 @@ +namespace WIC +{ + public enum ExifColorSpace : int + { + SRGB = 1, + AdobeSRGB = 2, + } +} diff --git a/src/modules/peek/WIC/Enumerations/LOCKTYPE.cs b/src/modules/peek/WIC/Enumerations/LOCKTYPE.cs new file mode 100644 index 0000000000..9ce372bb98 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/LOCKTYPE.cs @@ -0,0 +1,14 @@ +using System; +using System.ComponentModel; + +namespace WIC +{ + [Flags] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public enum LOCKTYPE : int + { + LOCK_WRITE = 1, + LOCK_EXCLUSIVE = 2, + LOCK_ONLYONCE = 4, + } +} diff --git a/src/modules/peek/WIC/Enumerations/MetadataCreationAndPersistOptions.cs b/src/modules/peek/WIC/Enumerations/MetadataCreationAndPersistOptions.cs new file mode 100644 index 0000000000..01a10508ac --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/MetadataCreationAndPersistOptions.cs @@ -0,0 +1,19 @@ +using System; + +namespace WIC +{ + [Flags] + public enum MetadataCreationAndPersistOptions : int + { + WICMetadataCreationDefault = 0x00000000, + WICMetadataCreationAllowUnknown = WICMetadataCreationDefault, + WICMetadataCreationFailUnknown = 0x00010000, + + WICPersistOptionDefault = 0x00000000, + WICPersistOptionLittleEndian = 0x00000000, + WICPersistOptionBigEndian = 0x00000001, + WICPersistOptionStrictFormat = 0x00000002, + WICPersistOptionNoCacheStream = 0x00000004, + WICPersistOptionPreferUTF8 = 0x00000008, + } +} diff --git a/src/modules/peek/WIC/Enumerations/STATFLAG.cs b/src/modules/peek/WIC/Enumerations/STATFLAG.cs new file mode 100644 index 0000000000..8f6afee364 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/STATFLAG.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public enum STATFLAG : int + { + STATFLAG_DEFAULT = 0, + STATFLAG_NONAME = 1, + STATFLAG_NOOPEN = 2, + } +} diff --git a/src/modules/peek/WIC/Enumerations/STGC.cs b/src/modules/peek/WIC/Enumerations/STGC.cs new file mode 100644 index 0000000000..4010f835f5 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/STGC.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel; + +namespace WIC +{ + [Flags] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public enum STGC : int + { + STGC_DEFAULT = 0, + STGC_OVERWRITE = 1, + STGC_ONLYIFCURRENT = 2, + STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4, + STGC_CONSOLIDATE = 8, + } +} diff --git a/src/modules/peek/WIC/Enumerations/STGM.cs b/src/modules/peek/WIC/Enumerations/STGM.cs new file mode 100644 index 0000000000..03cdb33e0b --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/STGM.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel; + +namespace WIC +{ + [Flags] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public enum STGM : int + { + STGM_READ = 0x00000000, + STGM_WRITE = 0x00000001, + STGM_READWRITE = 0x00000002, + #warning `STGM`: Enumeration incomplete. Consider adding all values. + } +} diff --git a/src/modules/peek/WIC/Enumerations/STGTY.cs b/src/modules/peek/WIC/Enumerations/STGTY.cs new file mode 100644 index 0000000000..989332553b --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/STGTY.cs @@ -0,0 +1,13 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public enum STGTY : int + { + STGTY_STORAGE = 1, + STGTY_STREAM = 2, + STGTY_LOCKBYTES = 3, + STGTY_PROPERTY = 4, + } +} diff --git a/src/modules/peek/WIC/Enumerations/STREAM_SEEK.cs b/src/modules/peek/WIC/Enumerations/STREAM_SEEK.cs new file mode 100644 index 0000000000..e92c2aab92 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/STREAM_SEEK.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public enum STREAM_SEEK : int + { + STREAM_SEEK_SET = 0, + STREAM_SEEK_CUR = 1, + STREAM_SEEK_END = 2, + } +} diff --git a/src/modules/peek/WIC/Enumerations/StreamAccessMode.cs b/src/modules/peek/WIC/Enumerations/StreamAccessMode.cs new file mode 100644 index 0000000000..e79321d0df --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/StreamAccessMode.cs @@ -0,0 +1,11 @@ +using System; + +namespace WIC +{ + [Flags] + public enum StreamAccessMode : int + { + GENERIC_WRITE = 0x40000000, + GENERIC_READ = unchecked((int)0x80000000U), + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapAlphaChannelOption.cs b/src/modules/peek/WIC/Enumerations/WICBitmapAlphaChannelOption.cs new file mode 100644 index 0000000000..4623c5605e --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapAlphaChannelOption.cs @@ -0,0 +1,9 @@ +namespace WIC +{ + public enum WICBitmapAlphaChannelOption : int + { + WICBitmapUseAlpha = 0x00000000, + WICBitmapUsePremultipliedAlpha = 0x00000001, + WICBitmapIgnoreAlpha = 0x00000002, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapCreateCacheOption.cs b/src/modules/peek/WIC/Enumerations/WICBitmapCreateCacheOption.cs new file mode 100644 index 0000000000..09bf9b71ea --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapCreateCacheOption.cs @@ -0,0 +1,9 @@ +namespace WIC +{ + public enum WICBitmapCreateCacheOption : int + { + WICBitmapNoCache = 0x00000000, + WICBitmapCacheOnDemand = 0x00000001, + WICBitmapCacheOnLoad = 0x00000002, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapDecoderCapabilities.cs b/src/modules/peek/WIC/Enumerations/WICBitmapDecoderCapabilities.cs new file mode 100644 index 0000000000..19b9c30652 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapDecoderCapabilities.cs @@ -0,0 +1,14 @@ +using System; + +namespace WIC +{ + [Flags] + public enum WICBitmapDecoderCapabilities : int + { + WICBitmapDecoderCapabilitySameEncoder = 0x00000001, + WICBitmapDecoderCapabilityCanDecodeAllImages = 0x00000002, + WICBitmapDecoderCapabilityCanDecodeSomeImages = 0x00000004, + WICBitmapDecoderCapabilityCanEnumerateMetadata = 0x00000008, + WICBitmapDecoderCapabilityCanDecodeThumbnail = 0x00000010, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapDitherType.cs b/src/modules/peek/WIC/Enumerations/WICBitmapDitherType.cs new file mode 100644 index 0000000000..c9441c6686 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapDitherType.cs @@ -0,0 +1,19 @@ +namespace WIC +{ + public enum WICBitmapDitherType : int + { + WICBitmapDitherTypeNone = 0x00000000, + WICBitmapDitherTypeSolid = 0x00000000, + + WICBitmapDitherTypeOrdered4x4 = 0x00000001, + + WICBitmapDitherTypeOrdered8x8 = 0x00000002, + WICBitmapDitherTypeOrdered16x16 = 0x00000003, + WICBitmapDitherTypeSpiral4x4 = 0x00000004, + WICBitmapDitherTypeSpiral8x8 = 0x00000005, + WICBitmapDitherTypeDualSpiral4x4 = 0x00000006, + WICBitmapDitherTypeDualSpiral8x8 = 0x00000007, + + WICBitmapDitherTypeErrorDiffusion = 0x00000008, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapEncoderCacheOption.cs b/src/modules/peek/WIC/Enumerations/WICBitmapEncoderCacheOption.cs new file mode 100644 index 0000000000..922235cfee --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapEncoderCacheOption.cs @@ -0,0 +1,9 @@ +namespace WIC +{ + public enum WICBitmapEncoderCacheOption : int + { + WICBitmapEncoderCacheInMemory = 0x00000000, + WICBitmapEncoderCacheTempFile = 0x00000001, + WICBitmapEncoderNoCache = 0x00000002, + } +} \ No newline at end of file diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapInterpolationMode.cs b/src/modules/peek/WIC/Enumerations/WICBitmapInterpolationMode.cs new file mode 100644 index 0000000000..85a17f4f48 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapInterpolationMode.cs @@ -0,0 +1,14 @@ +namespace WIC +{ + public enum WICBitmapInterpolationMode : int + { + WICBitmapInterpolationModeNearestNeighbor = 0x00000000, + WICBitmapInterpolationModeLinear = 0x00000001, + WICBitmapInterpolationModeCubic = 0x00000002, + WICBitmapInterpolationModeFant = 0x00000003, + /// + /// Supported beginning with Windows 10. + /// + WICBitmapInterpolationModeHighQualityCubic = 0x00000004, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapLockFlags.cs b/src/modules/peek/WIC/Enumerations/WICBitmapLockFlags.cs new file mode 100644 index 0000000000..06ff6ebde1 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapLockFlags.cs @@ -0,0 +1,11 @@ +using System; + +namespace WIC +{ + [Flags] + public enum WICBitmapLockFlags : int + { + WICBitmapLockRead = 0x00000001, + WICBitmapLockWrite = 0x00000002, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapPaletteType.cs b/src/modules/peek/WIC/Enumerations/WICBitmapPaletteType.cs new file mode 100644 index 0000000000..4a7a81bf72 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapPaletteType.cs @@ -0,0 +1,20 @@ +namespace WIC +{ + public enum WICBitmapPaletteType : int + { + WICBitmapPaletteTypeCustom = 0x00000000, + WICBitmapPaletteTypeMedianCut = 0x00000001, + WICBitmapPaletteTypeFixedBW = 0x00000002, + WICBitmapPaletteTypeFixedHalftone8 = 0x00000003, + WICBitmapPaletteTypeFixedHalftone27 = 0x00000004, + WICBitmapPaletteTypeFixedHalftone64 = 0x00000005, + WICBitmapPaletteTypeFixedHalftone125 = 0x00000006, + WICBitmapPaletteTypeFixedHalftone216 = 0x00000007, + WICBitmapPaletteTypeFixedWebPalette = WICBitmapPaletteTypeFixedHalftone216, + WICBitmapPaletteTypeFixedHalftone252 = 0x00000008, + WICBitmapPaletteTypeFixedHalftone256 = 0x00000009, + WICBitmapPaletteTypeFixedGray4 = 0x0000000A, + WICBitmapPaletteTypeFixedGray16 = 0x0000000B, + WICBitmapPaletteTypeFixedGray256 = 0x0000000C, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICBitmapTransformOptions.cs b/src/modules/peek/WIC/Enumerations/WICBitmapTransformOptions.cs new file mode 100644 index 0000000000..474d7480c3 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICBitmapTransformOptions.cs @@ -0,0 +1,17 @@ +using System; + +namespace WIC +{ + [Flags] + public enum WICBitmapTransformOptions : int + { + WICBitmapTransformRotate0 = 0x00000000, + WICBitmapTransformRotate90 = 0x00000001, + WICBitmapTransformRotate180 = 0x00000002, + WICBitmapTransformRotate270 = 0x00000003, + + WICBitmapTransformFlipHorizontal = 0x00000008, + + WICBitmapTransformFlipVertical = 0x00000010, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICColorContextType.cs b/src/modules/peek/WIC/Enumerations/WICColorContextType.cs new file mode 100644 index 0000000000..836f88d9fa --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICColorContextType.cs @@ -0,0 +1,9 @@ +namespace WIC +{ + public enum WICColorContextType : int + { + WICColorContextUninitialized = 0x00000000, + WICColorContextProfile = 0x00000001, + WICColorContextExifColorSpace = 0x00000002, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICComponentEnumerateOptions.cs b/src/modules/peek/WIC/Enumerations/WICComponentEnumerateOptions.cs new file mode 100644 index 0000000000..2159ac2b8d --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICComponentEnumerateOptions.cs @@ -0,0 +1,14 @@ +using System; + +namespace WIC +{ + [Flags] + public enum WICComponentEnumerateOptions : int + { + WICComponentEnumerateDefault = 0x00000000, + WICComponentEnumerateRefresh = 0x00000001, + WICComponentEnumerateBuiltInOnly = 0x20000000, + WICComponentEnumerateUnsigned = 0x40000000, + WICComponentEnumerateDisabled = unchecked((int)0x80000000U), + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICComponentSigning.cs b/src/modules/peek/WIC/Enumerations/WICComponentSigning.cs new file mode 100644 index 0000000000..68bc08516f --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICComponentSigning.cs @@ -0,0 +1,13 @@ +using System; + +namespace WIC +{ + [Flags] + public enum WICComponentSigning : int + { + WICComponentSigned = 0x00000001, + WICComponentUnsigned = 0x00000002, + WICComponentSafe = 0x00000004, + WICComponentDisabled = unchecked((int)0x80000000U), + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICComponentType.cs b/src/modules/peek/WIC/Enumerations/WICComponentType.cs new file mode 100644 index 0000000000..0448c85129 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICComponentType.cs @@ -0,0 +1,16 @@ +using System; + +namespace WIC +{ + [Flags] + public enum WICComponentType : int + { + WICDecoder = 0x00000001, + WICEncoder = 0x00000002, + WICPixelFormatConverter = 0x00000004, + WICMetadataReader = 0x00000008, + WICMetadataWriter = 0x00000010, + WICPixelFormat = 0x00000020, + WICAllComponents = 0x0000003F, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICDecodeOptions.cs b/src/modules/peek/WIC/Enumerations/WICDecodeOptions.cs new file mode 100644 index 0000000000..84ba889161 --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICDecodeOptions.cs @@ -0,0 +1,8 @@ +namespace WIC +{ + public enum WICDecodeOptions : int + { + WICDecodeMetadataCacheOnDemand = 0x00000000, + WICDecodeMetadataCacheOnLoad = 0x00000001, + } +} diff --git a/src/modules/peek/WIC/Enumerations/WICMetadataCreationOptions.cs b/src/modules/peek/WIC/Enumerations/WICMetadataCreationOptions.cs new file mode 100644 index 0000000000..f6c2bc3c2b --- /dev/null +++ b/src/modules/peek/WIC/Enumerations/WICMetadataCreationOptions.cs @@ -0,0 +1,13 @@ +using System; + +namespace WIC +{ + [Flags] + public enum WICMetadataCreationOptions : int + { + WICMetadataCreationDefault = 0x00000000, + WICMetadataCreationAllowUnknown = WICMetadataCreationDefault, + WICMetadataCreationFailUnknown = 0x00010000, + WICMetadataCreationMask = unchecked((int)0xFFFF0000), + } +} diff --git a/src/modules/peek/WIC/Extensions/IEnumStringExtensions.cs b/src/modules/peek/WIC/Extensions/IEnumStringExtensions.cs new file mode 100644 index 0000000000..35c507c365 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IEnumStringExtensions.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IEnumStringExtensions + { + public static IEnumerable AsEnumerable(this IEnumString enumString) + { + var buffer = new string[1]; + for (;;) + { + int length; + enumString.Next(1, buffer, out length); + if (length != 1) break; + yield return buffer[0]; + } + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IEnumUnknownExtensions.cs b/src/modules/peek/WIC/Extensions/IEnumUnknownExtensions.cs new file mode 100644 index 0000000000..a581a343e6 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IEnumUnknownExtensions.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IEnumUnknownExtensions + { + public static IEnumerable AsEnumerable(this IEnumUnknown enumUnknown) + { + var buffer = new object[1]; + for (;;) + { + int length; + enumUnknown.Next(1, buffer, out length); + if (length != 1) break; + yield return buffer[0]; + } + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IStreamExtensions.cs b/src/modules/peek/WIC/Extensions/IStreamExtensions.cs new file mode 100644 index 0000000000..fdbec608e9 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IStreamExtensions.cs @@ -0,0 +1,67 @@ +using System; +using System.ComponentModel; +using Marshal = System.Runtime.InteropServices.Marshal; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IStreamExtensions + { + public static void Read(this IStream stream, byte[] pv, int cb) + { + stream.Read(pv, cb, IntPtr.Zero); + } + + public static void Read(this IStream stream, byte[] pv, int cb, out int pcbRead) + { + var pcbReadPtr = Marshal.AllocCoTaskMem(4); + try + { + stream.Read(pv, cb, pcbReadPtr); + pcbRead = Marshal.ReadInt32(pcbReadPtr); + } + finally + { + Marshal.FreeCoTaskMem(pcbReadPtr); + } + } + + public static void Seek(this IStream stream, long dlibMove, STREAM_SEEK dwOrigin) + { + stream.Seek(dlibMove, dwOrigin, IntPtr.Zero); + } + + public static void Seek(this IStream stream, long dlibMove, STREAM_SEEK dwOrigin, out long plibNewPosition) + { + var plibNewPositionPtr = Marshal.AllocCoTaskMem(8); + try + { + stream.Seek(dlibMove, dwOrigin, plibNewPositionPtr); + plibNewPosition = Marshal.ReadInt64(plibNewPositionPtr, 0); + } + finally + { + Marshal.FreeCoTaskMem(plibNewPositionPtr); + } + } + + public static void Write(this IStream stream, byte[] pv, int cb) + { + stream.Write(pv, cb, IntPtr.Zero); + } + + public static void Write(this IStream stream, byte[] pv, int cb, out int pcbWritten) + { + var pcbWrittenPtr = Marshal.AllocCoTaskMem(4); + try + { + stream.Read(pv, cb, pcbWrittenPtr); + pcbWritten = Marshal.ReadInt32(pcbWrittenPtr); + } + finally + { + Marshal.FreeCoTaskMem(pcbWrittenPtr); + } + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapCodecInfoExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapCodecInfoExtensions.cs new file mode 100644 index 0000000000..d411d7fd04 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapCodecInfoExtensions.cs @@ -0,0 +1,38 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapCodecInfoExtensions + { + public static string GetColorManagementVersion(this IWICBitmapCodecInfo bitmapCodecInfo) + { + FetchIntoBuffer fetcher = bitmapCodecInfo.GetColorManagementVersion; + return fetcher.FetchString(); + } + + public static string GetDeviceManufacturer(this IWICBitmapCodecInfo bitmapCodecInfo) + { + FetchIntoBuffer fetcher = bitmapCodecInfo.GetDeviceManufacturer; + return fetcher.FetchString(); + } + + public static string GetDeviceModels(this IWICBitmapCodecInfo bitmapCodecInfo) + { + FetchIntoBuffer fetcher = bitmapCodecInfo.GetDeviceModels; + return fetcher.FetchString(); + } + + public static string[] GetMimeTypes(this IWICBitmapCodecInfo bitmapCodecInfo) + { + FetchIntoBuffer fetcher = bitmapCodecInfo.GetMimeTypes; + return fetcher.FetchString().Split(','); + } + + public static string[] GetFileExtensions(this IWICBitmapCodecInfo bitmapCodecInfo) + { + FetchIntoBuffer fetcher = bitmapCodecInfo.GetFileExtensions; + return fetcher.FetchString().Split(','); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapDecoderExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapDecoderExtensions.cs new file mode 100644 index 0000000000..d0ce32611b --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapDecoderExtensions.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapDecoderExtensions + { + public static IWICMetadataBlockReader AsMetadataBlockReader(this IWICBitmapDecoder bitmapDecoder) + { + return bitmapDecoder as IWICMetadataBlockReader; + } + + public static IWICColorContext[] GetColorContexts(this IWICBitmapDecoder bitmapDecoder) + { + int length; + bitmapDecoder.GetColorContexts(0, null, out length); + var colorContexts = new IWICColorContext[length]; + if (length > 0) + { + bitmapDecoder.GetColorContexts(length, colorContexts, out length); + } + return colorContexts; + } + + public static IEnumerable GetFrames(this IWICBitmapDecoder bitmapDecoder) + { + for (int i = 0, n = bitmapDecoder.GetFrameCount(); i < n; ++i) + { + yield return bitmapDecoder.GetFrame(i); + } + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapDecoderInfoExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapDecoderInfoExtensions.cs new file mode 100644 index 0000000000..2b2f37ff26 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapDecoderInfoExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapDecoderInfoExtensions + { + public static WICBitmapPattern[] GetPatterns(this IWICBitmapDecoderInfo bitmapDecoderInfo) + { + int count; + int size; + bitmapDecoderInfo.GetPatterns(0, IntPtr.Zero, out count, out size); + if (count == 0) + { + return new WICBitmapPattern[0]; + } + using (var buffer = new CoTaskMemPtr(Marshal.AllocCoTaskMem(size))) + { + bitmapDecoderInfo.GetPatterns(size, buffer, out count, out size); + IntPtr at = buffer; + var patterns = new WICBitmapPattern[count]; + for (int i = 0, stride = Marshal.SizeOf(typeof(WICBitmapPatternRaw)); i < count; ++i, at += stride) + { + var raw = (WICBitmapPatternRaw)Marshal.PtrToStructure(at, typeof(WICBitmapPatternRaw)); + int length = raw.Length; + patterns[i] = new WICBitmapPattern() + { + Length = length, + Position = raw.Position, + Pattern = new byte[length], + Mask = new byte[length], + EndOfStream = raw.EndOfStream + }; + Marshal.Copy(raw.Pattern, patterns[i].Pattern, 0, length); + Marshal.Copy(raw.Mask, patterns[i].Mask, 0, length); + } + return patterns; + } + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapEncoderExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapEncoderExtensions.cs new file mode 100644 index 0000000000..b0df316ba7 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapEncoderExtensions.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapEncoderExtensions + { + public static IWICMetadataBlockWriter AsMetadataBlockWriter(this IWICBitmapEncoder bitmapEncoder) + { + return bitmapEncoder as IWICMetadataBlockWriter; + } + + public static IWICBitmapFrameEncode CreateNewFrame(this IWICBitmapEncoder bitmapEncoder, IPropertyBag2 ppIEncoderOptions = null) + { + IWICBitmapFrameEncode ppIFrameEncode; + bitmapEncoder.CreateNewFrame(out ppIFrameEncode, ppIEncoderOptions); + return ppIFrameEncode; + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapExtensions.cs new file mode 100644 index 0000000000..e98916a0df --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapExtensions.cs @@ -0,0 +1,21 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapExtensions + { + public static IWICBitmapLock Lock(this IWICBitmap bitmap, WICBitmapLockFlags flags, WICRect? prcLock = null) + { + using (var prcLockPtr = CoTaskMemPtr.From(prcLock)) + { + return bitmap.Lock(prcLockPtr, flags); + } + } + + public static void SetResolution(this IWICBitmap bitmap, Resolution resolution) + { + bitmap.SetResolution(resolution.DpiX, resolution.DpiY); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapFrameDecodeExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapFrameDecodeExtensions.cs new file mode 100644 index 0000000000..3504168845 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapFrameDecodeExtensions.cs @@ -0,0 +1,37 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapFrameDecodeExtensions + { + public static IWICMetadataBlockReader AsMetadataBlockReader(this IWICBitmapFrameDecode bitmapFrameDecode) + { + return bitmapFrameDecode as IWICMetadataBlockReader; + } + + public static IWICColorContext[] GetColorContexts(this IWICBitmapFrameDecode bitmapFrameDecode) + { + FetchIntoBuffer fetcher = bitmapFrameDecode.GetColorContexts; + return fetcher.FetchArray(); + } + + public static void Initialize(this IWICBitmapFrameEncode bitmapFrameEncode, IPropertyBag2 pIEncoderOptions = null) + { + bitmapFrameEncode.Initialize(pIEncoderOptions); + } + + public static void WriteSource(this IWICBitmapFrameEncode bitmapFrameEncode, IWICBitmapSource pIBitmapSource, WICRect? prc = null) + { + using (var prcPtr = CoTaskMemPtr.From(prc)) + { + bitmapFrameEncode.WriteSource(pIBitmapSource, prcPtr); + } + } + + public static void WritePixels(this IWICBitmapFrameEncode bitmapFrameEncode, int lineCount, int cbStride, byte[] pbPixels) + { + bitmapFrameEncode.WritePixels(lineCount, cbStride, pbPixels.Length, pbPixels); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapFrameEncodeExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapFrameEncodeExtensions.cs new file mode 100644 index 0000000000..1945fad163 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapFrameEncodeExtensions.cs @@ -0,0 +1,46 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapFrameEncodeExtensions + { + public static IWICMetadataBlockWriter AsMetadataBlockWriter(this IWICBitmapFrameEncode bitmapFrameEncode) + { + return bitmapFrameEncode as IWICMetadataBlockWriter; + } + + public static void Initialize(this IWICBitmapFrameEncode bitmapFrameEncode, IPropertyBag2 pIEncoderOptions = null) + { + bitmapFrameEncode.Initialize(pIEncoderOptions); + } + + public static void SetColorContexts(this IWICBitmapFrameEncode bitmapFrameEncode, IWICColorContext[] colorContexts) + { + bitmapFrameEncode.SetColorContexts(colorContexts.Length, colorContexts); + } + + public static void SetResolution(this IWICBitmapFrameEncode bitmapFrameEncode, Resolution resolution) + { + bitmapFrameEncode.SetResolution(resolution.DpiX, resolution.DpiY); + } + + public static void SetSize(this IWICBitmapFrameEncode bitmapFrameEncode, Size size) + { + bitmapFrameEncode.SetSize(size.Width, size.Height); + } + + public static void WriteSource(this IWICBitmapFrameEncode bitmapFrameEncode, IWICBitmapSource pIBitmapSource, WICRect? prc = null) + { + using (var prcPtr = CoTaskMemPtr.From(prc)) + { + bitmapFrameEncode.WriteSource(pIBitmapSource, prcPtr); + } + } + + public static void WritePixels(this IWICBitmapFrameEncode bitmapFrameEncode, int lineCount, int cbStride, byte[] pbPixels) + { + bitmapFrameEncode.WritePixels(lineCount, cbStride, pbPixels.Length, pbPixels); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapLockExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapLockExtensions.cs new file mode 100644 index 0000000000..433afd9787 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapLockExtensions.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapLockExtensions + { + public static Size GetSize(this IWICBitmapLock bitmapLock) + { + int width, height; + bitmapLock.GetSize(out width, out height); + return new Size(width, height); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapScalerExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapScalerExtensions.cs new file mode 100644 index 0000000000..aaed69c46f --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapScalerExtensions.cs @@ -0,0 +1,13 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapScalerExtensions + { + public static void Initialize(this IWICBitmapScaler bitmapScaler, IWICBitmapSource pISource, Size size, WICBitmapInterpolationMode mode) + { + bitmapScaler.Initialize(pISource, size.Width, size.Height, mode); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICBitmapSourceExtensions.cs b/src/modules/peek/WIC/Extensions/IWICBitmapSourceExtensions.cs new file mode 100644 index 0000000000..54e9f57e3f --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICBitmapSourceExtensions.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICBitmapSourceExtensions + { + public static void CopyPixels(this IWICBitmapSource bitmapSource, int cbStride, byte[] pbBuffer, WICRect? prc = null) + { + using (var prcPtr = CoTaskMemPtr.From(prc)) + { + bitmapSource.CopyPixels(prcPtr, cbStride, pbBuffer.Length, pbBuffer); + } + } + + public static Size GetSize(this IWICBitmapSource bitmapSource) + { + int width, height; + bitmapSource.GetSize(out width, out height); + return new Size(width, height); + } + + public static Resolution GetResolution(this IWICBitmapSource bitmapSource) + { + double dpiX, dpiY; + bitmapSource.GetResolution(out dpiX, out dpiY); + return new Resolution(dpiX, dpiY); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICColorContextExtensions.cs b/src/modules/peek/WIC/Extensions/IWICColorContextExtensions.cs new file mode 100644 index 0000000000..1d9b8498ec --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICColorContextExtensions.cs @@ -0,0 +1,19 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICColorContextExtensions + { + public static void InitializeFromMemory(this IWICColorContext colorContext, byte[] pbBuffer) + { + colorContext.InitializeFromMemory(pbBuffer, pbBuffer.Length); + } + + public static byte[] GetProfileBytes(this IWICColorContext colorContext) + { + FetchIntoBuffer fetcher = colorContext.GetProfileBytes; + return fetcher.FetchArray(); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICComponentInfoExtensions.cs b/src/modules/peek/WIC/Extensions/IWICComponentInfoExtensions.cs new file mode 100644 index 0000000000..29fe3c50d0 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICComponentInfoExtensions.cs @@ -0,0 +1,32 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICComponentInfoExtensions + { + public static string GetAuthor(this IWICComponentInfo componentInfo) + { + FetchIntoBuffer fetcher = componentInfo.GetAuthor; + return fetcher.FetchString(); + } + + public static string GetFriendlyName(this IWICComponentInfo componentInfo) + { + FetchIntoBuffer fetcher = componentInfo.GetFriendlyName; + return fetcher.FetchString(); + } + + public static string GetVersion(this IWICComponentInfo componentInfo) + { + FetchIntoBuffer fetcher = componentInfo.GetVersion; + return fetcher.FetchString(); + } + + public static string GetSpecVersion(this IWICComponentInfo componentInfo) + { + FetchIntoBuffer fetcher = componentInfo.GetSpecVersion; + return fetcher.FetchString(); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICFormatConverterInfoExtensions.cs b/src/modules/peek/WIC/Extensions/IWICFormatConverterInfoExtensions.cs new file mode 100644 index 0000000000..30581a1d63 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICFormatConverterInfoExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICFormatConverterInfoExtensions + { + public static Guid[] GetPixelFormats(this IWICFormatConverterInfo formatConverterInfo) + { + FetchIntoBuffer fetcher = formatConverterInfo.GetPixelFormats; + return fetcher.FetchArray(); + } + + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICImagingFactoryExtensions.cs b/src/modules/peek/WIC/Extensions/IWICImagingFactoryExtensions.cs new file mode 100644 index 0000000000..a266a0e7ed --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICImagingFactoryExtensions.cs @@ -0,0 +1,80 @@ +using System; +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICImagingFactoryExtensions + { + public static IWICBitmap CreateBitmap(this IWICImagingFactory imagingFactory, Size size, Guid pixelFormat, WICBitmapCreateCacheOption option) + { + return imagingFactory.CreateBitmap(size.Width, size.Height, pixelFormat, option); + } + + public static IWICBitmap CreateBitmapFromSourceRect(this IWICImagingFactory imagingFactory, IWICBitmapSource pIBitmapSource, WICRect rect) + { + return imagingFactory.CreateBitmapFromSourceRect(pIBitmapSource, rect.X, rect.Y, rect.Width, rect.Height); + } + + public static IWICBitmap CreateBitmapFromMemory(this IWICImagingFactory imagingFactory, Size size, Guid pixelFormat, int cbStride, byte[] pbBuffer) + { + return imagingFactory.CreateBitmapFromMemory(size.Width, size.Height, pixelFormat, cbStride, pbBuffer.Length, pbBuffer); + } + + public static IWICBitmapDecoder CreateDecoder(this IWICImagingFactory imagingFactory, Guid guidContainerFormat, Guid? pguidVendor = null) + { + using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) + { + return imagingFactory.CreateDecoder(guidContainerFormat, pguidVendorPtr); + } + } + + public static IWICBitmapDecoder CreateDecoderFromFileHandle(this IWICImagingFactory imagingFactory, IntPtr hFile, WICDecodeOptions metadataOptions, Guid? pguidVendor = null) + { + using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) + { + return imagingFactory.CreateDecoderFromFileHandle(hFile, pguidVendorPtr, metadataOptions); + } + } + + public static IWICBitmapDecoder CreateDecoderFromFilename(this IWICImagingFactory imagingFactory, string wzFilename, Guid? pguidVendor = null, WICDecodeOptions metadataOptions = WICDecodeOptions.WICDecodeMetadataCacheOnDemand) + { + using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) + { + return imagingFactory.CreateDecoderFromFilename(wzFilename, pguidVendorPtr, StreamAccessMode.GENERIC_READ, metadataOptions); + } + } + + public static IWICBitmapDecoder CreateDecoderFromStream(this IWICImagingFactory imagingFactory, IStream pIStream, WICDecodeOptions metadataOptions, Guid? pguidVendor = null) + { + using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) + { + return imagingFactory.CreateDecoderFromStream(pIStream, pguidVendorPtr, metadataOptions); + } + } + + public static IWICBitmapEncoder CreateEncoder(this IWICImagingFactory factory, Guid guidContainerFormat, Guid? pguidVendor = null) + { + using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) + { + return factory.CreateEncoder(guidContainerFormat, pguidVendorPtr); + } + } + + public static IWICMetadataQueryWriter CreateQueryWriter(this IWICImagingFactory imagingFactory, Guid guidMetadataFormat, Guid? pguidVendor = null) + { + using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) + { + return imagingFactory.CreateQueryWriter(guidMetadataFormat, pguidVendorPtr); + } + } + + public static IWICMetadataQueryWriter CreateQueryWriterFromReader(this IWICImagingFactory imagingFactory, IWICMetadataQueryReader pIQueryReader, Guid? pguidVendor = null) + { + using (var pguidVendorPtr = CoTaskMemPtr.From(pguidVendor)) + { + return imagingFactory.CreateQueryWriterFromReader(pIQueryReader, pguidVendorPtr); + } + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICMetadataHandlerInfoExtensions.cs b/src/modules/peek/WIC/Extensions/IWICMetadataHandlerInfoExtensions.cs new file mode 100644 index 0000000000..b503d2c40a --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICMetadataHandlerInfoExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICMetadataHandlerInfoExtensions + { + public static Guid[] GetContainerFormats(this IWICMetadataHandlerInfo metadataHandlerInfo) + { + FetchIntoBuffer fetcher = metadataHandlerInfo.GetContainerFormats; + return fetcher.FetchArray(); + } + + public static string GetDeviceManufacturer(this IWICMetadataHandlerInfo metadataHandlerInfo) + { + FetchIntoBuffer fetcher = metadataHandlerInfo.GetDeviceManufacturer; + return fetcher.FetchString(); + } + + public static string GetDeviceModels(this IWICMetadataHandlerInfo metadataHandlerInfo) + { + FetchIntoBuffer fetcher = metadataHandlerInfo.GetDeviceModels; + return fetcher.FetchString(); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICMetadataQueryReaderExtensions.cs b/src/modules/peek/WIC/Extensions/IWICMetadataQueryReaderExtensions.cs new file mode 100644 index 0000000000..a8a549087b --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICMetadataQueryReaderExtensions.cs @@ -0,0 +1,35 @@ +using System.ComponentModel; +using System.Runtime.InteropServices; +using static WIC.PropVariantHelpers; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICMetadataQueryReaderExtensions + { + public static string GetLocation(this IWICMetadataQueryReader metadataQueryReader) + { + FetchIntoBuffer fetcher = metadataQueryReader.GetLocation; + return fetcher.FetchString(); + } + + public static bool TryGetMetadataByName(this IWICMetadataQueryReader metadataQueryReader, string name, out T value) + { + var variant = new PROPVARIANT(); + try + { + metadataQueryReader.GetMetadataByName(name, ref variant); + return TryDecode(ref variant, out value); + } + catch (COMException ex) when (ex.ErrorCode == HResult.WINCODEC_ERR_PROPERTYNOTFOUND) + { + value = default(T); + return false; + } + finally + { + Dispose(ref variant); + } + } + } +} diff --git a/src/modules/peek/WIC/Extensions/IWICPaletteExtensions.cs b/src/modules/peek/WIC/Extensions/IWICPaletteExtensions.cs new file mode 100644 index 0000000000..e3b5941ad9 --- /dev/null +++ b/src/modules/peek/WIC/Extensions/IWICPaletteExtensions.cs @@ -0,0 +1,19 @@ +using System.ComponentModel; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class IWICPaletteExtensions + { + public static int[] GetColors(this IWICPalette palette) + { + FetchIntoBuffer fetcher = palette.GetColors; + return fetcher.FetchArray(); + } + + public static void InitializeCustom(this IWICPalette palette, int[] pColors) + { + palette.InitializeCustom(pColors, pColors.Length); + } + } +} diff --git a/src/modules/peek/WIC/Extensions/StreamExtensions.cs b/src/modules/peek/WIC/Extensions/StreamExtensions.cs new file mode 100644 index 0000000000..5b8a49727f --- /dev/null +++ b/src/modules/peek/WIC/Extensions/StreamExtensions.cs @@ -0,0 +1,129 @@ +using System; +using System.ComponentModel; +using System.IO; + +using Marshal = System.Runtime.InteropServices.Marshal; + +namespace WIC +{ + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static class StreamExtensions + { + public static IStream AsCOMStream(this Stream stream) + { + return new COMStream(stream); + } + + private sealed class COMStream : IStream + { + public COMStream(Stream underlyingStream) + { + UnderlyingStream = underlyingStream; + } + + private Stream UnderlyingStream { get; } + + public void Read(byte[] pv, int cb, IntPtr pcbRead) + { + int bytesRead = UnderlyingStream.Read(pv, 0, cb); + if (pcbRead != IntPtr.Zero) + { + Marshal.WriteInt32(pcbRead, 0, bytesRead); + } + } + + public void Write(byte[] pv, int cb, IntPtr pcbWritten) + { + var bytesWritten = Math.Min(pv.Length, cb); + UnderlyingStream.Write(pv, 0, bytesWritten); + if (pcbWritten != IntPtr.Zero) + { + Marshal.WriteInt32(pcbWritten, 0, bytesWritten); + } + } + + public void Seek(long dlibMove, STREAM_SEEK dwOrigin, IntPtr plibNewPosition) + { + SeekOrigin seekOrigin; + switch (dwOrigin) + { + case STREAM_SEEK.STREAM_SEEK_SET: + seekOrigin = SeekOrigin.Begin; + break; + case STREAM_SEEK.STREAM_SEEK_CUR: + seekOrigin = SeekOrigin.Current; + break; + case STREAM_SEEK.STREAM_SEEK_END: + seekOrigin = SeekOrigin.End; + break; + default: + throw new ArgumentOutOfRangeException(nameof(dwOrigin)); + } + long newPosition = UnderlyingStream.Seek(dlibMove, seekOrigin); + if (plibNewPosition != IntPtr.Zero) + { + Marshal.WriteInt64(plibNewPosition, 0, newPosition); + } + } + + public void SetSize(long libNewSize) + { + UnderlyingStream.SetLength(libNewSize); + } + + void IStream.CopyTo(IStream pstm, long cb, out long pcbRead, out long pcbWritten) + { + throw new System.NotImplementedException(); + } + + void IStream.Commit(STGC grfCommitFlags) + { + throw new System.NotImplementedException(); + } + + void IStream.Revert() + { + throw new System.NotImplementedException(); + } + + void IStream.LockRegion(long libOffset, long cb, LOCKTYPE dwLockType) + { + throw new System.NotImplementedException(); + } + + void IStream.UnlockRegion(long libOffset, long cb, LOCKTYPE dwLockType) + { + throw new System.NotImplementedException(); + } + + public void Stat(ref STATSTG pstatstg, STATFLAG grfStatFlag) + { + pstatstg = new STATSTG(); + pstatstg.type = STGTY.STGTY_STREAM; + pstatstg.cbSize = UnderlyingStream.Length; + pstatstg.grfMode = 0; + if (UnderlyingStream.CanRead && UnderlyingStream.CanWrite) + { + pstatstg.grfMode |= STGM.STGM_READWRITE; + } + else if (UnderlyingStream.CanRead) + { + pstatstg.grfMode |= STGM.STGM_READ; + } + else if (UnderlyingStream.CanWrite) + { + pstatstg.grfMode |= STGM.STGM_WRITE; + } + else + { + throw new ObjectDisposedException(null); + } + } + + IStream IStream.Clone() + { + throw new System.NotImplementedException(); + } + } + } +} diff --git a/src/modules/peek/WIC/FetchIntoBufferMethod.cs b/src/modules/peek/WIC/FetchIntoBufferMethod.cs new file mode 100644 index 0000000000..236e20ad91 --- /dev/null +++ b/src/modules/peek/WIC/FetchIntoBufferMethod.cs @@ -0,0 +1,37 @@ +namespace WIC +{ + internal delegate void FetchIntoBuffer(int size, T[] buffer, out int length); + + internal static class FetchIntoBufferExtensions + { + internal static T[] FetchArray(this FetchIntoBuffer fetcher) + { + int length; + fetcher.Invoke(0, null, out length); + var buffer = new T[length]; + if (length > 0) + { + fetcher.Invoke(length, buffer, out length); + } + return buffer; + } + + internal static string FetchString(this FetchIntoBuffer fetcher) + { + var buffer = fetcher.FetchArray(); + int length = buffer.Length - 1; + if (length > 0) + { + return new string(buffer, 0, length); + } + else if (length == 0) + { + return string.Empty; + } + else + { + return null; + } + } + } +} diff --git a/src/modules/peek/WIC/Interfaces/IEnumString.cs b/src/modules/peek/WIC/Interfaces/IEnumString.cs new file mode 100644 index 0000000000..a2dbd14213 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IEnumString.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IEnumString)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumString + { + void Next( + [In] int celt, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] string[] rgelt, + [Out] out int pceltFetched); + + void Skip( + [In] int celt); + + void Reset(); + + IEnumString Clone(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IEnumUnknown.cs b/src/modules/peek/WIC/Interfaces/IEnumUnknown.cs new file mode 100644 index 0000000000..a06b2bfff8 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IEnumUnknown.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IEnumUnknown)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEnumUnknown + { + void Next( + [In] int celt, + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface, SizeParamIndex = 0)] object[] rgelt, + [Out] out int pceltFetched); + + void Skip( + [In] int celt); + + void Reset(); + + IEnumUnknown Clone(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IErrorLog.cs b/src/modules/peek/WIC/Interfaces/IErrorLog.cs new file mode 100644 index 0000000000..6649c55940 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IErrorLog.cs @@ -0,0 +1,15 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IErrorLog)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IErrorLog + { + void AddError( + [In, MarshalAs(UnmanagedType.LPWStr)] string pszPropName, + [In] IntPtr pExcepInfo); // EXCEPINFO* + } +} diff --git a/src/modules/peek/WIC/Interfaces/IID.cs b/src/modules/peek/WIC/Interfaces/IID.cs new file mode 100644 index 0000000000..9792e3a3f0 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IID.cs @@ -0,0 +1,43 @@ +namespace WIC +{ + internal static class IID + { + public const string IEnumString = "00000101-0000-0000-c000-000000000046"; + public const string IEnumUnknown = "00000100-0000-0000-c000-000000000046"; + public const string IErrorLog = "3127ca40-446e-11ce-8135-00aa004bb851"; + public const string IPropertyBag2 = "22f55882-280b-11d0-a8a9-00a0c90c2004"; + public const string ISequentialStream = "0c733a30-2a1c-11ce-ade5-00aa0044773d"; + public const string IStream = "0000000c-0000-0000-c000-000000000046"; + public const string IWICBitmap = "00000121-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICBitmapClipper = "e4fbcf03-223d-4e81-9333-d635556dd1b5"; + public const string IWICBitmapCodecInfo = "e87a44c4-b76e-4c47-8b09-298eb12a2714"; + public const string IWICBitmapDecoder = "9edde9e7-8dee-47ea-99df-e6faf2ed44bf"; + public const string IWICBitmapDecoderInfo = "d8cd007f-d08f-4191-9bfc-236ea7f0e4b5"; + public const string IWICBitmapEncoder = "00000103-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICBitmapEncoderInfo = "94c9b4ee-a09f-4f92-8a1e-4a9bce7e76fb"; + public const string IWICBitmapFlipRotator = "5009834f-2d6a-41ce-9e1b-17c5aff7a782"; + public const string IWICBitmapFrameDecode = "3b16811b-6a43-4ec9-a813-3d930c13b940"; + public const string IWICBitmapFrameEncode = "00000105-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICBitmapLock = "00000123-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICBitmapScaler = "00000302-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICBitmapSource = "00000120-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICColorContext = "3c613a02-34b2-44ea-9a7c-45aea9c6fd6d"; + public const string IWICColorTransform = "b66f034f-d0e2-40ab-b436-6de39e321a94"; + public const string IWICComponentFactory = "412d0c3a-9650-44fa-af5b-dd2a06c8e8fb"; + public const string IWICComponentInfo = "23bc3f0a-698b-4357-886b-f24d50671334"; + public const string IWICEnumMetadataItem = "dc2bb46d-3f07-481e-8625-220c4aedbb33"; + public const string IWICFastMetadataEncoder = "b84e2c09-78c9-4ac4-8bd3-524ae1663a2f"; + public const string IWICFormatConverter = "00000301-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICFormatConverterInfo = "9f34fb65-13f4-4f15-bc57-3726b5e53d9f"; + public const string IWICImagingFactory = "ec5ec8a9-c395-4314-9c77-54d7a935ff70"; + public const string IWICMetadataBlockReader = "feaa2a8d-b3f3-43e4-b25c-d1de990a1ae1"; + public const string IWICMetadataBlockWriter = "08fb9676-b444-41e8-8dbe-6a53a542bff1"; + public const string IWICMetadataHandlerInfo = "aba958bf-c672-44d1-8d61-ce6df2e682c2"; + public const string IWICMetadataQueryReader = "30989668-e1c9-4597-b395-458eedb808df"; + public const string IWICMetadataQueryWriter = "a721791a-0def-4d06-bd91-2118bf1db10b"; + public const string IWICMetadataReader = "9204fe99-d8fc-4fd5-a001-9536b067a899"; + public const string IWICMetadataWriter = "f7836e16-3be0-470b-86bb-160d0aecd7de"; + public const string IWICPalette = "00000040-a8f2-4877-ba0a-fd2b6645fb94"; + public const string IWICStream = "135ff860-22b7-4ddf-b0f6-218f4f299a43"; + } +} diff --git a/src/modules/peek/WIC/Interfaces/IPropertyBag2.cs b/src/modules/peek/WIC/Interfaces/IPropertyBag2.cs new file mode 100644 index 0000000000..22bbefe5f3 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IPropertyBag2.cs @@ -0,0 +1,37 @@ +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IPropertyBag2)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IPropertyBag2 + { + void Read( + [In] int cProperties, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPBAG2[] pPropBag, + [In] IErrorLog pErrLog, + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)] object[] pvarValue, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] phrError + ); + + void Write( + [In] int cProperties, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPBAG2[] pPropBag, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)] object[] pvarValue); + + int CountProperties(); + + void GetPropertyInfo( + [In] int iProperty, + [In] int cProperties, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] PROPBAG2[] pPropBag, + [Out] out int pcProperties); + + void LoadObject( + [In, MarshalAs(UnmanagedType.LPWStr)] string pstrName, + [In] int dwHint, + [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkObject, + [In] IErrorLog pErrLog); + } +} diff --git a/src/modules/peek/WIC/Interfaces/ISequentialStream.cs b/src/modules/peek/WIC/Interfaces/ISequentialStream.cs new file mode 100644 index 0000000000..e7badba8b7 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/ISequentialStream.cs @@ -0,0 +1,22 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.ISequentialStream)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface ISequentialStream + { + void Read( + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pv, + [In] int cb, + [Out] IntPtr pcbRead); + + void Write( + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pv, + [In] int cb, + [Out] IntPtr pcbWritten); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IStream.cs b/src/modules/peek/WIC/Interfaces/IStream.cs new file mode 100644 index 0000000000..29e352345c --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IStream.cs @@ -0,0 +1,61 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IStream)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IStream : ISequentialStream + { + #region Members inherited from `ISequentialStream` + + new void Read( + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pv, + [In] int cb, + [Out] IntPtr pcbRead); + + new void Write( + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pv, + [In] int cb, + [Out] IntPtr pcbWritten); + + #endregion + + void Seek( + [In] long dlibMove, + [In] STREAM_SEEK dwOrigin, + [Out] IntPtr plibNewPosition); + + void SetSize( + [In] long libNewSize); + + void CopyTo( + [In] IStream pstm, + [In] long cb, + [Out] out long pcbRead, + [Out] out long pcbWritten); + + void Commit( + [In] STGC grfCommitFlags); + + void Revert(); + + void LockRegion( + [In] long libOffset, + [In] long cb, + [In] LOCKTYPE dwLockType); + + void UnlockRegion( + [In] long libOffset, + [In] long cb, + [In] LOCKTYPE dwLockType); + + void Stat( + [In, Out] ref STATSTG pstatstg, + [In] STATFLAG grfStatFlag); + + IStream Clone(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmap.cs b/src/modules/peek/WIC/Interfaces/IWICBitmap.cs new file mode 100644 index 0000000000..f2a405a101 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmap.cs @@ -0,0 +1,45 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmap)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmap : IWICBitmapSource + { + #region Members inherited from `IWICBitmapSource` + + new void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + new Guid GetPixelFormat(); + + new void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + new void CopyPalette( + [In] IWICPalette pIPalette); + + new void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + + #endregion + + IWICBitmapLock Lock( + [In] IntPtr prcLock, // WICRect* + [In] WICBitmapLockFlags flags); + + void SetPalette( + [In] IWICPalette pIPalette); + + void SetResolution( + [In] double dpiX, + [In] double dpiY); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapClipper.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapClipper.cs new file mode 100644 index 0000000000..5b592e78c5 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapClipper.cs @@ -0,0 +1,38 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapClipper)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapClipper : IWICBitmapSource + { + #region Members inherited from `IWICBitmapSource` + + new void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + new Guid GetPixelFormat(); + + new void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + new void CopyPalette( + [In] IWICPalette pIPalette); + + new void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + + #endregion + + void Initialize( + [In] IWICBitmapSource pISource, + [In, MarshalAs(UnmanagedType.LPStruct)] WICRect prc); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapCodecInfo.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapCodecInfo.cs new file mode 100644 index 0000000000..118d12c548 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapCodecInfo.cs @@ -0,0 +1,86 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapCodecInfo)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapCodecInfo : IWICComponentInfo + { + #region Members inherited from `IWICComponentInfo` + + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new void GetAuthor( + [In] int cchAuthor, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzAuthor, + [Out] out int pcchActual); + + new Guid GetVendorGUID(); + + new void GetVersion( + [In] int cchVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzVersion, + [Out] out int pcchActual); + + new void GetSpecVersion( + [In] int cchSpecVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzSpecVersion, + [Out] out int pcchActual); + + new void GetFriendlyName( + [In] int cchFriendlyName, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFriendlyName, + [Out] out int pcchActual); + + #endregion + + Guid GetContainerFormat(); + + void GetPixelFormats( + [In] int cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] Guid[] pguidPixelFormats, + [Out] out int pcActual); + + void GetColorManagementVersion( + [In] int cchColorManagementVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzColorManagementVersion, + [Out] out int pcchActual); + + void GetDeviceManufacturer( + [In] int cchDeviceManufacturer, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceManufacturer, + [Out] out int pcchActual); + + void GetDeviceModels( + [In] int cchDeviceModels, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceModels, + [Out] out int pcchActual); + + void GetMimeTypes( + [In] int cchMimeTypes, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzMimeTypes, + [Out] out int pcchActual); + + void GetFileExtensions( + [In] int cchFileExtensions, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFileExtensions, + [Out] out int pcchActual); + + bool DoesSupportAnimation(); + + bool DoesSupportChromakey(); + + bool DoesSupportLossless(); + + bool DoesSupportMultiframe(); + + bool MatchesMimeType( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzMimeType); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapDecoder.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapDecoder.cs new file mode 100644 index 0000000000..208ee1be32 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapDecoder.cs @@ -0,0 +1,41 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapDecoder)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapDecoder + { + WICBitmapDecoderCapabilities QueryCapability( + [In] IStream pIStream); + + void Initialize( + [In] IStream pIStream, + [In] WICDecodeOptions cacheOptions); + + Guid GetContainerFormat(); + + IWICBitmapDecoderInfo GetDecoderInfo(); + + void CopyPalette( + [In] IWICPalette pIPalette); + + IWICMetadataQueryReader GetMetadataQueryReader(); + + IWICBitmapSource GetPreview(); + + void GetColorContexts( + [In] int cCount, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface, SizeParamIndex = 0)] IWICColorContext[] ppIColorContexts, + [Out] out int pcActualCount); + + IWICBitmapSource GetThumbnail(); + + int GetFrameCount(); + + IWICBitmapFrameDecode GetFrame( + [In] int index); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapDecoderInfo.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapDecoderInfo.cs new file mode 100644 index 0000000000..f0f169cf73 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapDecoderInfo.cs @@ -0,0 +1,102 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapDecoderInfo)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapDecoderInfo : IWICBitmapCodecInfo + { + #region Members inherited from `IWICBitmapCodecInfo` + + #region Members inherited from `IWICComponentInfo` + + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new void GetAuthor( + [In] int cchAuthor, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzAuthor, + [Out] out int pcchActual); + + new Guid GetVendorGUID(); + + new void GetVersion( + [In] int cchVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzVersion, + [Out] out int pcchActual); + + new void GetSpecVersion( + [In] int cchSpecVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzSpecVersion, + [Out] out int pcchActual); + + new void GetFriendlyName( + [In] int cchFriendlyName, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFriendlyName, + [Out] out int pcchActual); + + #endregion + + new Guid GetContainerFormat(); + + new void GetPixelFormats( + [In] int cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] Guid[] pguidPixelFormats, + [Out] out int pcActual); + + new void GetColorManagementVersion( + [In] int cchColorManagementVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzColorManagementVersion, + [Out] out int pcchActual); + + new void GetDeviceManufacturer( + [In] int cchDeviceManufacturer, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceManufacturer, + [Out] out int pcchActual); + + new void GetDeviceModels( + [In] int cchDeviceModels, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceModels, + [Out] out int pcchActual); + + new void GetMimeTypes( + [In] int cchMimeTypes, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzMimeTypes, + [Out] out int pcchActual); + + new void GetFileExtensions( + [In] int cchFileExtensions, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFileExtensions, + [Out] out int pcchActual); + + new bool DoesSupportAnimation(); + + new bool DoesSupportChromakey(); + + new bool DoesSupportLossless(); + + new bool DoesSupportMultiframe(); + + new bool MatchesMimeType( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzMimeType); + + #endregion + + void GetPatterns( + [In] int cbSizePatterns, + [In] IntPtr pPatterns, + [Out] out int pcPatterns, + [Out] out int pcbPatternsActual); + + bool MatchesPattern( + [In] IStream pIStream); + + IWICBitmapDecoder CreateInstance(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapEncoder.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapEncoder.cs new file mode 100644 index 0000000000..a7be9b1ff0 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapEncoder.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapEncoder)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapEncoder + { + void Initialize( + [In] IStream pIStream, + [In] WICBitmapEncoderCacheOption cacheOption); + + Guid GetContainerFormat(); + + IWICBitmapEncoderInfo GetEncoderInfo(); + + void SetColorContexts( + [In] int cCount, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface, SizeParamIndex = 0)] IWICColorContext[] ppIColorContext); + + void SetPalette( + [In] IWICPalette pIPalette); + + void SetThumbnail( + [In] IWICBitmapSource pIThumbnail); + + void SetPreview( + [In] IWICBitmapSource pIPreview); + + void CreateNewFrame( + [Out] out IWICBitmapFrameEncode ppIFrameEncode, + [In, Out] IPropertyBag2 ppIEncoderOptions); + + void Commit(); + + IWICMetadataQueryWriter GetMetadataQueryWriter(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapEncoderInfo.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapEncoderInfo.cs new file mode 100644 index 0000000000..dae17dbde6 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapEncoderInfo.cs @@ -0,0 +1,92 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapEncoderInfo)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapEncoderInfo : IWICBitmapCodecInfo + { + #region Members inherited from `IWICBitmapCodecInfo` + + #region Members inherited from `IWICComponentInfo` + + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new void GetAuthor( + [In] int cchAuthor, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzAuthor, + [Out] out int pcchActual); + + new Guid GetVendorGUID(); + + new void GetVersion( + [In] int cchVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzVersion, + [Out] out int pcchActual); + + new void GetSpecVersion( + [In] int cchSpecVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzSpecVersion, + [Out] out int pcchActual); + + new void GetFriendlyName( + [In] int cchFriendlyName, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFriendlyName, + [Out] out int pcchActual); + + #endregion + + new Guid GetContainerFormat(); + + new void GetPixelFormats( + [In] int cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] Guid[] pguidPixelFormats, + [Out] out int pcActual); + + new void GetColorManagementVersion( + [In] int cchColorManagementVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzColorManagementVersion, + [Out] out int pcchActual); + + new void GetDeviceManufacturer( + [In] int cchDeviceManufacturer, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceManufacturer, + [Out] out int pcchActual); + + new void GetDeviceModels( + [In] int cchDeviceModels, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceModels, + [Out] out int pcchActual); + + new void GetMimeTypes( + [In] int cchMimeTypes, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzMimeTypes, + [Out] out int pcchActual); + + new void GetFileExtensions( + [In] int cchFileExtensions, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFileExtensions, + [Out] out int pcchActual); + + new bool DoesSupportAnimation(); + + new bool DoesSupportChromakey(); + + new bool DoesSupportLossless(); + + new bool DoesSupportMultiframe(); + + new bool MatchesMimeType( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzMimeType); + + #endregion + + IWICBitmapEncoder CreateInstance(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapFlipRotator.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapFlipRotator.cs new file mode 100644 index 0000000000..69ee777dac --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapFlipRotator.cs @@ -0,0 +1,38 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapFlipRotator)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapFlipRotator : IWICBitmapSource + { + #region Members inherited from `IWICBitmapSource` + + new void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + new Guid GetPixelFormat(); + + new void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + new void CopyPalette( + [In] IWICPalette pIPalette); + + new void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + + #endregion + + void Initialize( + [In] IWICBitmapSource pISource, + [In] WICBitmapTransformOptions options); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapFrameDecode.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapFrameDecode.cs new file mode 100644 index 0000000000..0a7e4f3896 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapFrameDecode.cs @@ -0,0 +1,43 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapFrameDecode)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapFrameDecode : IWICBitmapSource + { + #region Members inherited from `IWICBitmapSource` + + new void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + new Guid GetPixelFormat(); + + new void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + new void CopyPalette( + [In] IWICPalette pIPalette); + + new void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + + #endregion + + IWICMetadataQueryReader GetMetadataQueryReader(); + + void GetColorContexts( + [In] int cCount, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface, SizeParamIndex = 0)] IWICColorContext[] ppIColorContexts, + [Out] out int pcActualCount); + + IWICBitmapSource GetThumbnail(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapFrameEncode.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapFrameEncode.cs new file mode 100644 index 0000000000..c2d74e096b --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapFrameEncode.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapFrameEncode)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapFrameEncode + { + void Initialize( + [In] IPropertyBag2 pIEncoderOptions); + + void SetSize( + [In] int uiWidth, + [In] int uiHeight); + + void SetResolution( + [In] double dpiX, + [In] double dpiY); + + void SetPixelFormat( + [In, Out] ref Guid pPixelFormat); + + void SetColorContexts( + [In] int cCount, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface, SizeParamIndex = 0)] IWICColorContext[] ppIColorContext); + + void SetPalette( + [In] IWICPalette pIPalette); + + void SetThumbnail( + [In] IWICBitmapSource pIThumbnail); + + void WritePixels( + [In] int lineCount, + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbPixels); + + void WriteSource( + [In] IWICBitmapSource pIBitmapSource, + [In] IntPtr prc); + + void Commit(); + + IWICMetadataQueryWriter GetMetadataQueryWriter(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapLock.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapLock.cs new file mode 100644 index 0000000000..3815e53fcb --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapLock.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapLock)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapLock + { + void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + int GetStride(); + + IntPtr GetDataPointer( // byte* + [Out] out int pcbBufferSize); + + Guid GetPixelFormat(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapScaler.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapScaler.cs new file mode 100644 index 0000000000..fe7e496e9c --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapScaler.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapScaler)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapScaler : IWICBitmapSource + { + #region Members inherited from `IWICBitmapSource` + + new void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + new Guid GetPixelFormat(); + + new void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + new void CopyPalette( + [In] IWICPalette pIPalette); + + new void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + + #endregion + + void Initialize( + [In] IWICBitmapSource pISource, + [In] int uiWidth, + [In] int uiHeight, + [In] WICBitmapInterpolationMode mode); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICBitmapSource.cs b/src/modules/peek/WIC/Interfaces/IWICBitmapSource.cs new file mode 100644 index 0000000000..96947d1651 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICBitmapSource.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICBitmapSource)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICBitmapSource + { + void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + Guid GetPixelFormat(); + + void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + void CopyPalette( + [In] IWICPalette pIPalette); + + void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICColorContext.cs b/src/modules/peek/WIC/Interfaces/IWICColorContext.cs new file mode 100644 index 0000000000..c0c0a736fd --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICColorContext.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICColorContext)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICColorContext + { + void InitializeFromFilename( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzFilename); + + void InitializeFromMemory( + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pbBuffer, + [In] int cbBufferSize); + + void InitializeFromExifColorSpace( + [In] ExifColorSpace value); + + WICColorContextType GetType(); + + void GetProfileBytes( + [In] int cbBuffer, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 0)] byte[] pbBuffer, + [Out] out int pcbActual); + + ExifColorSpace GetExifColorSpace(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICColorTransform.cs b/src/modules/peek/WIC/Interfaces/IWICColorTransform.cs new file mode 100644 index 0000000000..3312436a3c --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICColorTransform.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICColorTransform)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICColorTransform : IWICBitmapSource + { + #region Members inherited from `IWICBitmapSource` + + new void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + new Guid GetPixelFormat(); + + new void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + new void CopyPalette( + [In] IWICPalette pIPalette); + + new void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + + #endregion + + void Initialize( + [In] IWICBitmapSource pIBitmapSource, + [In] IWICColorContext pIContextSource, + [In] IWICColorContext pIContextDest, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid pixelFmtDest); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICComponentFactory.cs b/src/modules/peek/WIC/Interfaces/IWICComponentFactory.cs new file mode 100644 index 0000000000..4a5391bebe --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICComponentFactory.cs @@ -0,0 +1,140 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICComponentFactory)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICComponentFactory : IWICImagingFactory + { + #region Members inherited from `IWICImagingFactory` + + new IWICBitmapDecoder CreateDecoderFromFilename( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzFilename, + [In] IntPtr pguidVendor, + [In] StreamAccessMode dwDesiredAccess, + [In] WICDecodeOptions metadataOptions); + + new IWICBitmapDecoder CreateDecoderFromStream( + [In] IStream pIStream, + [In] IntPtr pguidVendor, + [In] WICDecodeOptions metadataOptions); + + new IWICBitmapDecoder CreateDecoderFromFileHandle( + [In] IntPtr hFile, + [In] IntPtr pguidVendor, + [In] WICDecodeOptions metadataOptions); + + new IWICComponentInfo CreateComponentInfo( + [In] Guid clsidComponent); + + new IWICBitmapDecoder CreateDecoder( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidContainerFormat, + [In] IntPtr pguidVendor); + + new IWICBitmapEncoder CreateEncoder( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidContainerFormat, + [In] IntPtr pguidVendor); + + new IWICPalette CreatePalette(); + + new IWICFormatConverter CreateFormatConverter(); + + new IWICBitmapScaler CreateBitmapScaler(); + + new IWICBitmapClipper CreateBitmapClipper(); + + new IWICBitmapFlipRotator CreateBitmapFlipRotator(); + + new IWICStream CreateStream(); + + new IWICColorContext CreateColorContext(); + + new IWICColorTransform CreateColorTransformer(); + + new IWICBitmap CreateBitmap( + [In] int uiWidth, + [In] int uiHeight, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid pixelFormat, + [In] WICBitmapCreateCacheOption option); + + new IWICBitmap CreateBitmapFromSource( + [In] IWICBitmapSource pIBitmapSource, + [In] WICBitmapCreateCacheOption option); + + new IWICBitmap CreateBitmapFromSourceRect( + [In] IWICBitmapSource pIBitmapSource, + [In] int x, + [In] int y, + [In] int width, + [In] int height); + + new IWICBitmap CreateBitmapFromMemory( + [In] int uiWidth, + [In] int uiHeight, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid pixelFormat, + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 4)] byte[] pbBuffer); + + new IWICBitmap CreateBitmapFromHBITMAP( + [In] IntPtr hBitmap, + [In] IntPtr hPalette, + [In] WICBitmapAlphaChannelOption options); + + new IWICBitmap CreateBitmapFromHICON( + [In] IntPtr hIcon); + + new IEnumUnknown CreateComponentEnumerator( + [In] WICComponentType componentTypes, + [In] WICComponentEnumerateOptions options); + + new IWICFastMetadataEncoder CreateFastMetadataEncoderFromDecoder( + [In] IWICBitmapDecoder pIDecoder); + + new IWICFastMetadataEncoder CreateFastMetadataEncoderFromFrameDecode( + [In] IWICBitmapFrameDecode pIFrameDecoder); + + new IWICMetadataQueryWriter CreateQueryWriter( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidMetadataFormat, + [In] IntPtr pguidVendor); + + new IWICMetadataQueryWriter CreateQueryWriterFromReader( + [In] IWICMetadataQueryReader pIQueryReader, + [In] IntPtr pguidVendor); + + #endregion + + IWICMetadataReader CreateMetadataReader( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidContainerFormat, + [In] IntPtr pguidVendor, + [In] MetadataCreationAndPersistOptions dwOptions, + [In] IStream pIStream); + + IWICMetadataReader CreateMetadataReaderFromContainer( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidContainerFormat, + [In] IntPtr pguidVendor, + [In] MetadataCreationAndPersistOptions dwOptions, + [In] IStream pIStream); + + IWICMetadataWriter CreateMetadataWriter( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidContainerFormat, + [In] IntPtr pguidVendor, + [In] WICMetadataCreationOptions dwMetadataOptions); + + IWICMetadataWriter CreateMetadataWriterFromReader( + [In] IWICMetadataReader pIReader, + [In] IntPtr pguidVendor); + + IWICMetadataQueryReader CreateQueryReaderFromBlockReader( + [In] IWICMetadataBlockReader pIBlockReader); + + IWICMetadataQueryWriter CreateQueryWriterFromBlockWriter( + [In] IWICMetadataBlockWriter pIBlockWriter); + + IPropertyBag2 CreateEncoderPropertyBag( + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] PROPBAG2[] ppropOptions, + [In] int cCount); + }; +} diff --git a/src/modules/peek/WIC/Interfaces/IWICComponentInfo.cs b/src/modules/peek/WIC/Interfaces/IWICComponentInfo.cs new file mode 100644 index 0000000000..c0e298c148 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICComponentInfo.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICComponentInfo)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICComponentInfo + { + WICComponentType GetComponentType(); + + Guid GetCLSID(); + + WICComponentSigning GetSigningStatus(); + + void GetAuthor( + [In] int cchAuthor, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzAuthor, + [Out] out int pcchActual); + + Guid GetVendorGUID(); + + void GetVersion( + [In] int cchVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzVersion, + [Out] out int pcchActual); + + void GetSpecVersion( + [In] int cchSpecVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzSpecVersion, + [Out] out int pcchActual); + + void GetFriendlyName( + [In] int cchFriendlyName, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFriendlyName, + [Out] out int pcchActual); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICEnumMetadataItem.cs b/src/modules/peek/WIC/Interfaces/IWICEnumMetadataItem.cs new file mode 100644 index 0000000000..0691fb76c0 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICEnumMetadataItem.cs @@ -0,0 +1,24 @@ +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICEnumMetadataItem)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICEnumMetadataItem + { + void Next( + [In] int celt, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPVARIANT[] rgeltSchema, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPVARIANT[] rgeltId, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPVARIANT[] rgeltValue, + [Out] out int pceltFetched); + + void Skip( + [In] int celt); + + void Reset(); + + IWICEnumMetadataItem Clone(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICFastMetadataEncoder.cs b/src/modules/peek/WIC/Interfaces/IWICFastMetadataEncoder.cs new file mode 100644 index 0000000000..bc79ce6a9d --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICFastMetadataEncoder.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICFastMetadataEncoder)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICFastMetadataEncoder + { + void Commit(); + + IWICMetadataQueryWriter GetMetadataQueryWriter(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICFormatConverter.cs b/src/modules/peek/WIC/Interfaces/IWICFormatConverter.cs new file mode 100644 index 0000000000..387e0431b2 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICFormatConverter.cs @@ -0,0 +1,46 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICFormatConverter)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICFormatConverter : IWICBitmapSource + { + #region Members inherited from `IWICBitmapSource` + + new void GetSize( + [Out] out int puiWidth, + [Out] out int puiHeight); + + new Guid GetPixelFormat(); + + new void GetResolution( + [Out] out double pDpiX, + [Out] out double pDpiY); + + new void CopyPalette( + [In] IWICPalette pIPalette); + + new void CopyPixels( + [In] IntPtr prc, // WICRect* + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] pbBuffer); + + #endregion + + void Initialize( + [In] IWICBitmapSource pISource, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid dstFormat, + [In] WICBitmapDitherType dither, + [In] IWICPalette pIPalette, + [In] double alphaThresholdPercent, + [In] WICBitmapPaletteType paletteTranslate); + + bool CanConvert( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid srcPixelFormat, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid dstPixelFormat); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICFormatConverterInfo.cs b/src/modules/peek/WIC/Interfaces/IWICFormatConverterInfo.cs new file mode 100644 index 0000000000..db5caee896 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICFormatConverterInfo.cs @@ -0,0 +1,50 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICFormatConverterInfo)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICFormatConverterInfo : IWICComponentInfo + { + #region Members inherited from `IWICComponentInfo` + + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new void GetAuthor( + [In] int cchAuthor, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzAuthor, + [Out] out int pcchActual); + + new Guid GetVendorGUID(); + + new void GetVersion( + [In] int cchVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzVersion, + [Out] out int pcchActual); + + new void GetSpecVersion( + [In] int cchSpecVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzSpecVersion, + [Out] out int pcchActual); + + new void GetFriendlyName( + [In] int cchFriendlyName, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFriendlyName, + [Out] out int pcchActual); + + #endregion + + void GetPixelFormats( + [In] int cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] Guid[] pPixelFormatGUIDs, + [Out] out int pcActual); + + IWICFormatConverter CreateInstance(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICImagingFactory.cs b/src/modules/peek/WIC/Interfaces/IWICImagingFactory.cs new file mode 100644 index 0000000000..f64149d03e --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICImagingFactory.cs @@ -0,0 +1,105 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICImagingFactory)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICImagingFactory + { + IWICBitmapDecoder CreateDecoderFromFilename( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzFilename, + [In] IntPtr pguidVendor, + [In] StreamAccessMode dwDesiredAccess, + [In] WICDecodeOptions metadataOptions); + + IWICBitmapDecoder CreateDecoderFromStream( + [In] IStream pIStream, + [In] IntPtr pguidVendor, + [In] WICDecodeOptions metadataOptions); + + IWICBitmapDecoder CreateDecoderFromFileHandle( + [In] IntPtr hFile, + [In] IntPtr pguidVendor, + [In] WICDecodeOptions metadataOptions); + + IWICComponentInfo CreateComponentInfo( + [In] Guid clsidComponent); + + IWICBitmapDecoder CreateDecoder( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidContainerFormat, + [In] IntPtr pguidVendor); + + IWICBitmapEncoder CreateEncoder( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidContainerFormat, + [In] IntPtr pguidVendor); + + IWICPalette CreatePalette(); + + IWICFormatConverter CreateFormatConverter(); + + IWICBitmapScaler CreateBitmapScaler(); + + IWICBitmapClipper CreateBitmapClipper(); + + IWICBitmapFlipRotator CreateBitmapFlipRotator(); + + IWICStream CreateStream(); + + IWICColorContext CreateColorContext(); + + IWICColorTransform CreateColorTransformer(); + + IWICBitmap CreateBitmap( + [In] int uiWidth, + [In] int uiHeight, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid pixelFormat, + [In] WICBitmapCreateCacheOption option); + + IWICBitmap CreateBitmapFromSource( + [In] IWICBitmapSource pIBitmapSource, + [In] WICBitmapCreateCacheOption option); + + IWICBitmap CreateBitmapFromSourceRect( + [In] IWICBitmapSource pIBitmapSource, + [In] int x, + [In] int y, + [In] int width, + [In] int height); + + IWICBitmap CreateBitmapFromMemory( + [In] int uiWidth, + [In] int uiHeight, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid pixelFormat, + [In] int cbStride, + [In] int cbBufferSize, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 4)] byte[] pbBuffer); + + IWICBitmap CreateBitmapFromHBITMAP( + [In] IntPtr hBitmap, + [In] IntPtr hPalette, + [In] WICBitmapAlphaChannelOption options); + + IWICBitmap CreateBitmapFromHICON( + [In] IntPtr hIcon); + + IEnumUnknown CreateComponentEnumerator( + [In] WICComponentType componentTypes, + [In] WICComponentEnumerateOptions options); + + IWICFastMetadataEncoder CreateFastMetadataEncoderFromDecoder( + [In] IWICBitmapDecoder pIDecoder); + + IWICFastMetadataEncoder CreateFastMetadataEncoderFromFrameDecode( + [In] IWICBitmapFrameDecode pIFrameDecoder); + + IWICMetadataQueryWriter CreateQueryWriter( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidMetadataFormat, + [In] IntPtr pguidVendor); + + IWICMetadataQueryWriter CreateQueryWriterFromReader( + [In] IWICMetadataQueryReader pIQueryReader, + [In] IntPtr pguidVendor); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICMetadataBlockReader.cs b/src/modules/peek/WIC/Interfaces/IWICMetadataBlockReader.cs new file mode 100644 index 0000000000..6259b5ef3e --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICMetadataBlockReader.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICMetadataBlockReader)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICMetadataBlockReader + { + Guid GetContainerFormat(); + + int GetCount(); + + IWICMetadataReader GetReaderByIndex( + [In] int nIndex); + + IEnumUnknown GetEnumerator(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICMetadataBlockWriter.cs b/src/modules/peek/WIC/Interfaces/IWICMetadataBlockWriter.cs new file mode 100644 index 0000000000..f4beeff383 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICMetadataBlockWriter.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICMetadataBlockWriter)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICMetadataBlockWriter : IWICMetadataBlockReader + { + #region Members inherited from `IWICMetadataBlockReader` + + new Guid GetContainerFormat(); + + new int GetCount(); + + new IWICMetadataReader GetReaderByIndex( + [In] int nIndex); + + new IEnumUnknown GetEnumerator(); + + #endregion + + void InitializeFromBlockReader( + [In] IWICMetadataBlockReader pIMDBlockReader); + + IWICMetadataWriter GetWriterByIndex( + [In] int nIndex); + + void AddWriter( + [In] IWICMetadataWriter pIMetadataWriter); + + void SetWriterByIndex( + [In] int nIndex, + [In] IWICMetadataWriter pIMetadataWriter); + + void RemoveWriterByIndex( + [In] int nIndex); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICMetadataHandlerInfo.cs b/src/modules/peek/WIC/Interfaces/IWICMetadataHandlerInfo.cs new file mode 100644 index 0000000000..b40ff222c1 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICMetadataHandlerInfo.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICMetadataHandlerInfo)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICMetadataHandlerInfo : IWICComponentInfo + { + #region Members inherited from `IWICComponentInfo` + + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new void GetAuthor( + [In] int cchAuthor, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzAuthor, + [Out] out int pcchActual); + + new Guid GetVendorGUID(); + + new void GetVersion( + [In] int cchVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzVersion, + [Out] out int pcchActual); + + new void GetSpecVersion( + [In] int cchSpecVersion, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzSpecVersion, + [Out] out int pcchActual); + + new void GetFriendlyName( + [In] int cchFriendlyName, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzFriendlyName, + [Out] out int pcchActual); + + #endregion + + Guid GetMetadataFormat(); + + void GetContainerFormats( + [In] int cContainerFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] Guid[] pguidContainerFormats, + [Out] out int pcchActual); + + void GetDeviceManufacturer( + [In] int cchDeviceManufacturer, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceManufacturer, + [Out] out int pcchActual); + + void GetDeviceModels( + [In] int cchDeviceModels, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzDeviceModels, + [Out] out int pcchActual); + + bool DoesRequireFullStream(); + + bool DoesSupportPadding(); + + bool DoesRequireFixedSize(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICMetadataQueryReader.cs b/src/modules/peek/WIC/Interfaces/IWICMetadataQueryReader.cs new file mode 100644 index 0000000000..d3f2af96c2 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICMetadataQueryReader.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICMetadataQueryReader)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICMetadataQueryReader + { + Guid GetContainerFormat(); + + void GetLocation( + [In] int cchMaxLength, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzNamespace, + [Out] out int pcchActualLength); + + void GetMetadataByName( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzName, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + + IEnumString GetEnumerator(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICMetadataQueryWriter.cs b/src/modules/peek/WIC/Interfaces/IWICMetadataQueryWriter.cs new file mode 100644 index 0000000000..30eb3021c1 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICMetadataQueryWriter.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICMetadataQueryWriter)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICMetadataQueryWriter : IWICMetadataQueryReader + { + #region Members inherited from `IWICMetadataQueryReader` + + new Guid GetContainerFormat(); + + new void GetLocation( + [In] int cchMaxLength, + [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeParamIndex = 0)] char[] wzNamespace, + [Out] out int pcchActualLength); + + new void GetMetadataByName( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzName, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + + new IEnumString GetEnumerator(); + + #endregion + + void SetMetadataByName( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzName, + [In, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + + void RemoveMetadataByName( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzName); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICMetadataReader.cs b/src/modules/peek/WIC/Interfaces/IWICMetadataReader.cs new file mode 100644 index 0000000000..473b7f5edc --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICMetadataReader.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICMetadataReader)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICMetadataReader + { + Guid GetMetadataFormat(); + + IWICMetadataHandlerInfo GetMetadataHandlerInfo(); + + int GetCount(); + + void GetValueByIndex( + [In] int nIndex, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarSchema, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarId, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + + void GetValue( + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarSchema, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarId, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + + IWICEnumMetadataItem GetEnumerator(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICMetadataWriter.cs b/src/modules/peek/WIC/Interfaces/IWICMetadataWriter.cs new file mode 100644 index 0000000000..23ffdee614 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICMetadataWriter.cs @@ -0,0 +1,52 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICMetadataWriter)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICMetadataWriter : IWICMetadataReader + { + #region Members inherited from `IWICMetadataReader` + + new Guid GetMetadataFormat(); + + new IWICMetadataHandlerInfo GetMetadataHandlerInfo(); + + new int GetCount(); + + new void GetValueByIndex( + [In] int nIndex, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarSchema, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarId, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + + new void GetValue( + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarSchema, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarId, + [In, Out, MarshalAs(UnmanagedType.Struct)] ref PROPVARIANT pvarValue); + + new IWICEnumMetadataItem GetEnumerator(); + + #endregion + + void SetValue( + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarSchema, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarId, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarValue); + + void SetValueByIndex( + [In] int nIndex, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarSchema, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarId, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarValue); + + void RemoveValue( + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarSchema, + [In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pvarId); + + void RemoveValueByIndex( + [In] int nIndex); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICPalette.cs b/src/modules/peek/WIC/Interfaces/IWICPalette.cs new file mode 100644 index 0000000000..4970ebbfb5 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICPalette.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICPalette)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICPalette + { + void InitializePredefined( + [In] WICBitmapPaletteType ePaletteType, + [In] bool fAddTransparentColor); + + void InitializeCustom( + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4, SizeParamIndex = 1)] int[] pColors, + [In] int cCount); + + void InitializeFromBitmap( + [In] IWICBitmapSource pISurface, + [In] int cCount, + [In] bool fAddTransparentColor); + + void InitializeFromPalette( + [In] IWICPalette pIPalette); + + WICBitmapPaletteType GetType(); + + int GetColorCount(); + + void GetColors( + [In] int cCount, + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4, SizeParamIndex = 0)] int[] pColors, + [Out] out int pcActualColors); + + bool IsBlackWhite(); + + bool IsGrayscale(); + + bool HasAlpha(); + } +} diff --git a/src/modules/peek/WIC/Interfaces/IWICStream.cs b/src/modules/peek/WIC/Interfaces/IWICStream.cs new file mode 100644 index 0000000000..093eed57f3 --- /dev/null +++ b/src/modules/peek/WIC/Interfaces/IWICStream.cs @@ -0,0 +1,80 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [ComImport] + [Guid(IID.IWICStream)] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWICStream : IStream + { + #region Members inherited from `IStream` + + #region Members inherited from `ISequentialStream` + + new void Read( + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pv, + [In] int cb, + [Out] IntPtr pcbRead); + + new void Write( + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pv, + [In] int cb, + [Out] IntPtr pcbWritten); + + #endregion + + new void Seek( + [In] long dlibMove, + [In] STREAM_SEEK dwOrigin, + [Out] IntPtr plibNewPosition); + + new void SetSize( + [In] long libNewSize); + + new void CopyTo( + [In] IStream pstm, + [In] long cb, + [Out] out long pcbRead, + [Out] out long pcbWritten); + + new void Commit( + [In] STGC grfCommitFlags); + + new void Revert(); + + new void LockRegion( + [In] long libOffset, + [In] long cb, + [In] LOCKTYPE dwLockType); + + new void UnlockRegion( + [In] long libOffset, + [In] long cb, + [In] LOCKTYPE dwLockType); + + new void Stat( + [In, Out] ref STATSTG pstatstg, + [In] STATFLAG grfStatFlag); + + new IStream Clone(); + + #endregion + + void InitializeFromIStream( + [In] IStream pIStream); + + void InitializeFromFilename( + [In, MarshalAs(UnmanagedType.LPWStr)] string wzFileName, + [In] StreamAccessMode dwDesiredAccess); + + void InitializeFromMemory( + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] pbBuffer, + [In] int cbBufferSize); + + void InitializeFromIStreamRegion( + [In] IStream pIStream, + [In] long ulOffset, + [In] long ulMaxSize); + } +} diff --git a/src/modules/peek/WIC/PropVariantHelpers.cs b/src/modules/peek/WIC/PropVariantHelpers.cs new file mode 100644 index 0000000000..2b2e755307 --- /dev/null +++ b/src/modules/peek/WIC/PropVariantHelpers.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace WIC +{ + internal static class PropVariantHelpers + { + static PropVariantHelpers() + { + Func getComObject = variant => + { + return Marshal.GetObjectForIUnknown(variant.Value.Ptr); + }; + + decoders = new Dictionary>() + { + [VARTYPE.VT_BOOL] = variant => variant.Value.UI2 == 0 ? false : true, + [VARTYPE.VT_UI1] = variant => variant.Value.UI1, + [VARTYPE.VT_UI2] = variant => variant.Value.UI2, + [VARTYPE.VT_UI4] = variant => variant.Value.UI4, + [VARTYPE.VT_UI8] = variant => variant.Value.UI8, + [VARTYPE.VT_I1] = variant => variant.Value.I1, + [VARTYPE.VT_I2] = variant => variant.Value.I2, + [VARTYPE.VT_I4] = variant => variant.Value.I4, + [VARTYPE.VT_I8] = variant => variant.Value.I8, + [VARTYPE.VT_LPSTR] = variant => Marshal.PtrToStringAnsi(variant.Value.Ptr), + [VARTYPE.VT_LPWSTR] = variant => Marshal.PtrToStringUni(variant.Value.Ptr), + [VARTYPE.VT_BSTR] = variant => Marshal.PtrToStringBSTR(variant.Value.Ptr), + [VARTYPE.VT_R4] = variant => variant.Value.R4, + [VARTYPE.VT_R8] = variant => variant.Value.R8, + [VARTYPE.VT_UNKNOWN] = variant => getComObject, + [VARTYPE.VT_STREAM] = variant => getComObject, + [VARTYPE.VT_STORAGE] = variant => getComObject, + [VARTYPE.VT_VECTOR] = DecodeVector, + }; + + Action disposePtr = variant => + { + Marshal.FreeCoTaskMem(variant.Value.Ptr); + }; + Action disposeBSTR = variant => + { + Marshal.FreeBSTR(variant.Value.Ptr); + }; + Action disposeComObject = variant => + { + Marshal.Release(variant.Value.Ptr); + }; + + disposers = new Dictionary>() + { + [VARTYPE.VT_LPSTR] = disposePtr, + [VARTYPE.VT_LPWSTR] = disposePtr, + [VARTYPE.VT_BSTR] = disposeBSTR, + [VARTYPE.VT_UNKNOWN] = disposeComObject, + [VARTYPE.VT_STREAM] = disposeComObject, + [VARTYPE.VT_STORAGE] = disposeComObject, + [VARTYPE.VT_VECTOR] = DisposeVector, + }; + } + + private static Dictionary> decoders; + private static Dictionary> disposers; + + public static bool TryDecode(ref PROPVARIANT variant, out T value) + { + const VARTYPE flagMask = VARTYPE.VT_ARRAY | VARTYPE.VT_VECTOR | VARTYPE.VT_BYREF; + bool hasFlag = (variant.Type & flagMask) != (VARTYPE)0; + Func decoder; + if (decoders.TryGetValue(variant.Type, out decoder) + || (hasFlag && decoders.TryGetValue(variant.Type & flagMask, out decoder))) + { + value = (T)decoder.Invoke(variant); + return true; + } + else + { + value = default(T); + return false; + } + } + + private static object DecodeVector(PROPVARIANT variant) + { + Type elementType; + int elementSize; + Func elementDecoder; + + switch (variant.Type & ~VARTYPE.VT_VECTOR) + { + case VARTYPE.VT_I1: + elementType = typeof(sbyte); + elementDecoder = ptr => (object)(sbyte)Marshal.ReadByte(ptr); + elementSize = 1; + break; + + case VARTYPE.VT_I2: + elementType = typeof(short); + elementDecoder = ptr => (object)Marshal.ReadInt16(ptr); + elementSize = 2; + break; + + case VARTYPE.VT_I4: + elementType = typeof(int); + elementDecoder = ptr => (object)Marshal.ReadInt32(ptr); + elementSize = 4; + break; + + case VARTYPE.VT_I8: + elementType = typeof(long); + elementDecoder = ptr => (object)Marshal.ReadInt64(ptr); + elementSize = 8; + break; + + case VARTYPE.VT_UI1: + elementType = typeof(byte); + elementDecoder = ptr => (object)Marshal.ReadByte(ptr); + elementSize = 1; + break; + + case VARTYPE.VT_UI2: + elementType = typeof(ushort); + elementDecoder = ptr => (object)(ushort)Marshal.ReadInt16(ptr); + elementSize = 2; + break; + + case VARTYPE.VT_UI4: + elementType = typeof(uint); + elementDecoder = ptr => (object)(uint)Marshal.ReadInt32(ptr); + elementSize = 4; + break; + + case VARTYPE.VT_UI8: + elementType = typeof(ulong); + elementDecoder = ptr => (object)(ulong)Marshal.ReadInt64(ptr); + elementSize = 8; + break; + + case VARTYPE.VT_LPSTR: + elementType = typeof(string); + elementDecoder = Marshal.PtrToStringAnsi; + elementSize = IntPtr.Size; + break; + + case VARTYPE.VT_LPWSTR: + elementType = typeof(string); + elementDecoder = Marshal.PtrToStringUni; + elementSize = IntPtr.Size; + break; + + case VARTYPE.VT_UNKNOWN: + case VARTYPE.VT_STREAM: + case VARTYPE.VT_STORAGE: + elementType = typeof(object); + elementDecoder = Marshal.GetObjectForIUnknown; + elementSize = IntPtr.Size; + break; + + default: + #warning `PropVariantHelpers.DecodeVector` does not yet know how to handle some `PROPVARIANT` types. + throw new System.NotImplementedException(); + } + + int length = variant.Value.Vector.Length; + var vector = Array.CreateInstance(elementType, length); + IntPtr elementPtr = variant.Value.Vector.Ptr; + for (int i = 0; i < length; ++i) + { + vector.SetValue(elementDecoder.Invoke(elementPtr), i); + elementPtr += elementSize; + } + + return vector; + } + + public static void Dispose(ref PROPVARIANT variant) + { + const VARTYPE flagMask = VARTYPE.VT_ARRAY | VARTYPE.VT_VECTOR | VARTYPE.VT_BYREF; + bool hasFlag = (variant.Type & flagMask) != (VARTYPE)0; + Action disposer; + if (disposers.TryGetValue(variant.Type, out disposer) + || (hasFlag && disposers.TryGetValue(variant.Type & flagMask, out disposer))) + { + disposer.Invoke(variant); + } + variant = new PROPVARIANT(); + } + + private static void DisposeVector(PROPVARIANT variant) + { + Action elementDisposer; + int elementSize; + + switch (variant.Type & ~VARTYPE.VT_VECTOR) + { + case VARTYPE.VT_BOOL: + case VARTYPE.VT_I1: + case VARTYPE.VT_I2: + case VARTYPE.VT_I4: + case VARTYPE.VT_I8: + case VARTYPE.VT_UI1: + case VARTYPE.VT_UI2: + case VARTYPE.VT_UI4: + case VARTYPE.VT_UI8: + case VARTYPE.VT_R4: + case VARTYPE.VT_R8: + elementDisposer = null; + elementSize = IntPtr.Size; + break; + + case VARTYPE.VT_BSTR: + elementDisposer = Marshal.FreeBSTR; + elementSize = IntPtr.Size; + break; + + case VARTYPE.VT_LPSTR: + case VARTYPE.VT_LPWSTR: + elementDisposer = Marshal.FreeCoTaskMem; + elementSize = IntPtr.Size; + break; + + case VARTYPE.VT_UNKNOWN: + case VARTYPE.VT_STREAM: + case VARTYPE.VT_STORAGE: + elementDisposer = ptr => { int ignored = Marshal.Release(ptr); }; + elementSize = IntPtr.Size; + break; + + default: + #warning `PropVariantHelpers.DisposeVector` does not yet know how to handle some `PROPVARIANT` types. + throw new System.NotImplementedException(); + } + + IntPtr vectorPtr = variant.Value.Vector.Ptr; + + // if necessary, dispose each of the vector's elements: + if (elementDisposer != null) + { + IntPtr elementPtr = vectorPtr; + for (int i = 0, n = variant.Value.Vector.Length; i < n; ++i) + { + elementDisposer.Invoke(elementPtr); + elementPtr += elementSize; + } + } + + // finally, dispose the vector array itself: + Marshal.FreeCoTaskMem(vectorPtr); + } + } +} diff --git a/src/modules/peek/WIC/Structures/PROPBAG2.cs b/src/modules/peek/WIC/Structures/PROPBAG2.cs new file mode 100644 index 0000000000..a133057399 --- /dev/null +++ b/src/modules/peek/WIC/Structures/PROPBAG2.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [StructLayout(LayoutKind.Sequential)] + public struct PROPBAG2 + { + public PROPBAG2_Type dwType; + public VARTYPE vt; + public short cfType; + public int dwHint; + public IntPtr pstrName; + public Guid clsid; + } + + public enum PROPBAG2_Type : int + { + PROPBAG2_TYPE_UNDEFINED = 0, + PROPBAG2_TYPE_DATA = 1, + PROPBAG2_TYPE_URL = 2, + PROPBAG2_TYPE_OBJECT = 3, + PROPBAG2_TYPE_STREAM = 4, + PROPBAG2_TYPE_STORAGE = 5, + PROPBAG2_TYPE_MONIKER = 6 + } +} diff --git a/src/modules/peek/WIC/Structures/PROPVARIANT.cs b/src/modules/peek/WIC/Structures/PROPVARIANT.cs new file mode 100644 index 0000000000..d8875a3fe6 --- /dev/null +++ b/src/modules/peek/WIC/Structures/PROPVARIANT.cs @@ -0,0 +1,79 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + public struct PROPVARIANT + { + public VARTYPE Type; + public PROPVARIANT_Value Value; + } + + [StructLayout(LayoutKind.Explicit)] + public struct PROPVARIANT_Value + { + [FieldOffset(0)] + public sbyte I1; + + [FieldOffset(0)] + public byte UI1; + + [FieldOffset(0)] + public short I2; + + [FieldOffset(0)] + public ushort UI2; + + [FieldOffset(0)] + public int I4; + + [FieldOffset(0)] + public uint UI4; + + [FieldOffset(0)] + public long I8; + + [FieldOffset(0)] + public ulong UI8; + + [FieldOffset(0)] + public PROPVARIANT_SplitI8 SplitI8; + + [FieldOffset(0)] + public PROPVARIANT_SplitUI8 SplitUI8; + + [FieldOffset(0)] + public float R4; + + [FieldOffset(0)] + public double R8; + + [FieldOffset(0)] + public IntPtr Ptr; + + [FieldOffset(0)] + public PROPVARIANT_Vector Vector; + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct PROPVARIANT_SplitI8 + { + public int A; + public int B; + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct PROPVARIANT_SplitUI8 + { + public uint A; + public uint B; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PROPVARIANT_Vector + { + public int Length; + public IntPtr Ptr; + } +} diff --git a/src/modules/peek/WIC/Structures/Resolution.cs b/src/modules/peek/WIC/Structures/Resolution.cs new file mode 100644 index 0000000000..0e8cde088d --- /dev/null +++ b/src/modules/peek/WIC/Structures/Resolution.cs @@ -0,0 +1,14 @@ +namespace WIC +{ + public struct Resolution + { + public Resolution(double dpiX, double dpiY) + { + DpiX = dpiX; + DpiY = dpiY; + } + + public double DpiX { get; } + public double DpiY { get; } + } +} diff --git a/src/modules/peek/WIC/Structures/STATSTG.cs b/src/modules/peek/WIC/Structures/STATSTG.cs new file mode 100644 index 0000000000..e6896d227d --- /dev/null +++ b/src/modules/peek/WIC/Structures/STATSTG.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WIC +{ + [StructLayout(LayoutKind.Sequential)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public struct STATSTG + { + [MarshalAs(UnmanagedType.LPWStr)] + public string pwcsName; + public STGTY type; + public long cbSize; + public long mtime; + public long ctime; + public long atime; + public STGM grfMode; + public LOCKTYPE grfLocksSupported; + public Guid clsid; + public int grfStateBits; + } +} diff --git a/src/modules/peek/WIC/Structures/Size.cs b/src/modules/peek/WIC/Structures/Size.cs new file mode 100644 index 0000000000..796b452de0 --- /dev/null +++ b/src/modules/peek/WIC/Structures/Size.cs @@ -0,0 +1,14 @@ +namespace WIC +{ + public struct Size + { + public Size(int width, int height) + { + Width = width; + Height = height; + } + + public int Width { get; } + public int Height { get; } + } +} diff --git a/src/modules/peek/WIC/Structures/VARTYPE.cs b/src/modules/peek/WIC/Structures/VARTYPE.cs new file mode 100644 index 0000000000..866d6bd13b --- /dev/null +++ b/src/modules/peek/WIC/Structures/VARTYPE.cs @@ -0,0 +1,61 @@ +using System; + +namespace WIC +{ + [Flags] + public enum VARTYPE : short + { + VT_EMPTY = 0, + VT_NULL = 1, + VT_I2 = 2, + VT_I4 = 3, + VT_R4 = 4, + VT_R8 = 5, + VT_CY = 6, + VT_DATE = 7, + VT_BSTR = 8, + VT_DISPATCH = 9, + VT_ERROR = 10, + VT_BOOL = 11, + VT_VARIANT = 12, + VT_UNKNOWN = 13, + VT_DECIMAL = 14, + VT_I1 = 16, + VT_UI1 = 17, + VT_UI2 = 18, + VT_UI4 = 19, + VT_I8 = 20, + VT_UI8 = 21, + VT_INT = 22, + VT_UINT = 23, + VT_VOID = 24, + VT_HRESULT = 25, + VT_PTR = 26, + VT_SAFEARRAY = 27, + VT_CARRAY = 28, + VT_USERDEFINED = 29, + VT_LPSTR = 30, + VT_LPWSTR = 31, + VT_RECORD = 36, + VT_INT_PTR = 37, + VT_UINT_PTR = 38, + VT_FILETIME = 64, + VT_BLOB = 65, + VT_STREAM = 66, + VT_STORAGE = 67, + VT_STREAMED_OBJECT = 68, + VT_STORED_OBJECT = 69, + VT_BLOB_OBJECT = 70, + VT_CF = 71, + VT_CLSID = 72, + VT_VERSIONED_STREAM = 73, + VT_BSTR_BLOB = 0xfff, + + VT_VECTOR = 0x1000, + VT_ARRAY = 0x2000, + VT_BYREF = 0x4000, + VT_RESERVED = unchecked((short)0x8000U), + + VT_ILLEGAL = unchecked((short)0xffffU), + } +} diff --git a/src/modules/peek/WIC/Structures/WICBitmapPattern.cs b/src/modules/peek/WIC/Structures/WICBitmapPattern.cs new file mode 100644 index 0000000000..c30b0af02c --- /dev/null +++ b/src/modules/peek/WIC/Structures/WICBitmapPattern.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.InteropServices; + +namespace WIC +{ + public sealed class WICBitmapPattern + { + public long Position; + public int Length; + public byte[] Pattern; + public byte[] Mask; + public bool EndOfStream; + } + + [StructLayout(LayoutKind.Sequential)] + internal sealed class WICBitmapPatternRaw + { + public long Position; + public int Length; + public IntPtr Pattern; + public IntPtr Mask; + public bool EndOfStream; + } +} diff --git a/src/modules/peek/WIC/Structures/WICRect.cs b/src/modules/peek/WIC/Structures/WICRect.cs new file mode 100644 index 0000000000..249ca8474b --- /dev/null +++ b/src/modules/peek/WIC/Structures/WICRect.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace WIC +{ + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)] + public struct WICRect + { + public int X; + public int Y; + public int Width; + public int Height; + } +} diff --git a/src/modules/peek/WIC/WIC.csproj b/src/modules/peek/WIC/WIC.csproj new file mode 100644 index 0000000000..75edaf6578 --- /dev/null +++ b/src/modules/peek/WIC/WIC.csproj @@ -0,0 +1,10 @@ + + + net7.0-windows10.0.19041.0 + 10.0.17763.0 + win10-x86;win10-x64;win10-arm64 + true + enable + false + + diff --git a/src/modules/peek/peek/dllmain.cpp b/src/modules/peek/peek/dllmain.cpp new file mode 100644 index 0000000000..bf334fe977 --- /dev/null +++ b/src/modules/peek/peek/dllmain.cpp @@ -0,0 +1,424 @@ +#include "pch.h" +#include +#include +#include +#include "trace.h" +#include +#include +#include + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + +namespace +{ + const wchar_t JSON_KEY_PROPERTIES[] = L"properties"; + const wchar_t JSON_KEY_WIN[] = L"win"; + const wchar_t JSON_KEY_ALT[] = L"alt"; + const wchar_t JSON_KEY_CTRL[] = L"ctrl"; + const wchar_t JSON_KEY_SHIFT[] = L"shift"; + const wchar_t JSON_KEY_CODE[] = L"code"; + const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut"; +} + +// The PowerToy name that will be shown in the settings. +const static wchar_t* MODULE_NAME = L"Peek"; +// Add a description that will we shown in the module settings page. +const static wchar_t* MODULE_DESC = L"A module that previews an image file."; + +// These are the properties shown in the Settings page. +struct ModuleSettings +{ + // Add the PowerToy module properties with default values. + // Currently available types: + // - int + // - bool + // - string + + //bool bool_prop = true; + //int int_prop = 10; + //std::wstring string_prop = L"The quick brown fox jumps over the lazy dog"; + //std::wstring color_prop = L"#1212FF"; + +} g_settings; + +// Implement the PowerToy Module Interface and all the required methods. +class Peek : public PowertoyModuleIface +{ +private: + // The PowerToy state. + bool m_enabled = false; + + Hotkey m_hotkey; + + HANDLE m_hProcess; + + HANDLE m_hInvokeEvent; + + // Load the settings file. + void init_settings() + { + try + { + // Load and parse the settings file for this PowerToy. + PowerToysSettings::PowerToyValues settings = + PowerToysSettings::PowerToyValues::load_from_settings_file(Peek::get_name()); + + parse_settings(settings); + + // Load a bool property. + //if (auto v = settings.get_bool_value(L"bool_toggle_1")) { + // g_settings.bool_prop = *v; + //} + + // Load an int property. + //if (auto v = settings.get_int_value(L"int_spinner_1")) { + // g_settings.int_prop = *v; + //} + + // Load a string property. + //if (auto v = settings.get_string_value(L"string_text_1")) { + // g_settings.string_prop = *v; + //} + + // Load a color property. + //if (auto v = settings.get_string_value(L"color_picker_1")) { + // g_settings.color_prop = *v; + //} + } + catch (std::exception&) + { + // Error while loading from the settings file. Let default values stay as they are. + } + } + + void parse_settings(PowerToysSettings::PowerToyValues& settings) + { + auto settingsObject = settings.get_raw_json(); + if (settingsObject.GetView().Size()) + { + try + { + auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT); + parse_hotkey(jsonHotkeyObject); + } + catch (...) + { + Logger::error("Failed to initialize Peek start settings"); + + set_default_settings(); + } + } + else + { + Logger::info("Peek settings are empty"); + + set_default_settings(); + } + } + + void set_default_settings() + { + Logger::info("Peek is going to use default settings"); + m_hotkey.win = false; + m_hotkey.alt = false; + m_hotkey.shift = false; + m_hotkey.ctrl = true; + m_hotkey.key = ' '; + } + + void parse_hotkey(winrt::Windows::Data::Json::JsonObject& jsonHotkeyObject) + { + try + { + m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN); + m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT); + m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT); + m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL); + m_hotkey.key = static_cast(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE)); + } + catch (...) + { + Logger::error("Failed to initialize Peek start shortcut"); + } + + if (!m_hotkey.key) + { + Logger::info("Peek is going to use default shortcut"); + m_hotkey.win = false; + m_hotkey.alt = false; + m_hotkey.shift = false; + m_hotkey.ctrl = true; + m_hotkey.key = ' '; + } + } + + bool is_viewer_running() + { + return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; + } + + void launch_process() + { + Logger::trace(L"Starting Peek.UI process"); + + unsigned long powertoys_pid = GetCurrentProcessId(); + + std::wstring executable_args = L""; + executable_args.append(std::to_wstring(powertoys_pid)); + + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + + sei.fMask = { SEE_MASK_NOCLOSEPROCESS }; + sei.lpVerb = L"open"; + sei.lpFile = L"modules\\Peek\\Powertoys.Peek.UI.exe"; + sei.nShow = SW_SHOWNORMAL; + sei.lpParameters = executable_args.data(); + + if (ShellExecuteExW(&sei)) + { + Logger::trace("Successfully started the PeekViewer process"); + } + else + { + Logger::error(L"PeekViewer failed to start. {}", get_last_error_or_default(GetLastError())); + } + + m_hProcess = sei.hProcess; + } + +public: + Peek() + { + init_settings(); + + m_hInvokeEvent = CreateDefaultEvent(CommonSharedConstants::SHOW_PEEK_SHARED_EVENT); + }; + + ~Peek() + { + if (m_enabled) + { + } + m_enabled = false; + }; + + // Destroy the powertoy and free memory + virtual void destroy() override + { + delete this; + } + + // Return the display name of the powertoy, this will be cached by the runner + virtual const wchar_t* get_name() override + { + return MODULE_NAME; + } + + virtual const wchar_t* get_key() override + { + return MODULE_NAME; + } + + // Return JSON with the configuration options. + virtual bool get_config(wchar_t* buffer, int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.set_description(MODULE_DESC); + + // Show an overview link in the Settings page + //settings.set_overview_link(L"https://"); + + // Show a video link in the Settings page. + //settings.set_video_link(L"https://"); + + // A bool property with a toggle editor. + //settings.add_bool_toogle( + // L"bool_toggle_1", // property name. + // L"This is what a BoolToggle property looks like", // description or resource id of the localized string. + // g_settings.bool_prop // property value. + //); + + // An integer property with a spinner editor. + //settings.add_int_spinner( + // L"int_spinner_1", // property name + // L"This is what a IntSpinner property looks like", // description or resource id of the localized string. + // g_settings.int_prop, // property value. + // 0, // min value. + // 100, // max value. + // 10 // incremental step. + //); + + // A string property with a textbox editor. + //settings.add_string( + // L"string_text_1", // property name. + // L"This is what a String property looks like", // description or resource id of the localized string. + // g_settings.string_prop // property value. + //); + + // A string property with a color picker editor. + //settings.add_color_picker( + // L"color_picker_1", // property name. + // L"This is what a ColorPicker property looks like", // description or resource id of the localized string. + // g_settings.color_prop // property value. + //); + + // A custom action property. When using this settings type, the "PowertoyModuleIface::call_custom_action()" + // method should be overriden as well. + //settings.add_custom_action( + // L"custom_action_id", // action name. + // L"This is what a CustomAction property looks like", // label above the field. + // L"Call a custom action", // button text. + // L"Press the button to call a custom action." // display values / extended info. + //); + + return settings.serialize_to_buffer(buffer, buffer_size); + } + + // Called by the runner to pass the updated settings values as a serialized JSON. + virtual void set_config(const wchar_t* config) override + { + try + { + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = + PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); + + parse_settings(values); + + values.save_to_settings_file(); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + // Enable the powertoy + virtual void enable() + { + ResetEvent(m_hInvokeEvent); + launch_process(); + m_enabled = true; + } + + // Disable the powertoy + virtual void disable() + { + if (m_enabled) + { + ResetEvent(m_hInvokeEvent); + TerminateProcess(m_hProcess, 1); + } + + m_enabled = false; + } + + // Returns if the powertoys is enabled + virtual bool is_enabled() override + { + return m_enabled; + } + + virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override + { + if (m_hotkey.key) + { + if (hotkeys && buffer_size >= 1) + { + hotkeys[0] = m_hotkey; + } + + return 1; + } + else + { + return 0; + } + } + + virtual bool on_hotkey(size_t hotkeyId) override + { + if (m_enabled) + { + Logger::trace(L"Peek hotkey pressed"); + + // TODO: fix VK_SPACE DestroyWindow in viewer app + if (!is_viewer_running()) + { + launch_process(); + } + + SetEvent(m_hInvokeEvent); + + return true; + } + + return false; + } +}; + +// This method of saving the module settings is only required if you need to do any +// custom processing of the settings before saving them to disk. +//void $projectname$::save_settings() { +// try { +// // Create a PowerToyValues object for this PowerToy +// PowerToysSettings::PowerToyValues values(get_name()); +// +// // Save a bool property. +// //values.add_property( +// // L"bool_toggle_1", // property name +// // g_settings.bool_prop // property value +// //); +// +// // Save an int property. +// //values.add_property( +// // L"int_spinner_1", // property name +// // g_settings.int_prop // property value +// //); +// +// // Save a string property. +// //values.add_property( +// // L"string_text_1", // property name +// // g_settings.string_prop // property value +// ); +// +// // Save a color property. +// //values.add_property( +// // L"color_picker_1", // property name +// // g_settings.color_prop // property value +// //); +// +// // Save the PowerToyValues JSON to the power toy settings file. +// values.save_to_settings_file(); +// } +// catch (std::exception ex) { +// // Couldn't save the settings. +// } +//} + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new Peek(); +} \ No newline at end of file diff --git a/src/modules/peek/peek/pch.cpp b/src/modules/peek/peek/pch.cpp new file mode 100644 index 0000000000..a83d3bb2cc --- /dev/null +++ b/src/modules/peek/peek/pch.cpp @@ -0,0 +1,2 @@ +#include "pch.h" +#pragma comment(lib, "windowsapp") \ No newline at end of file diff --git a/src/modules/peek/peek/pch.h b/src/modules/peek/peek/pch.h new file mode 100644 index 0000000000..eddac0fdc1 --- /dev/null +++ b/src/modules/peek/peek/pch.h @@ -0,0 +1,7 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/src/modules/peek/peek/peek.rc b/src/modules/peek/peek/peek.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/peek/peek/peek.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/peek/peek/peek.vcxproj b/src/modules/peek/peek/peek.vcxproj new file mode 100644 index 0000000000..b30da554d9 --- /dev/null +++ b/src/modules/peek/peek/peek.vcxproj @@ -0,0 +1,144 @@ + + + + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {a1425b53-3d61-4679-8623-e64a0d3d0a48} + Win32Proj + peek + 10.0.18362.0 + Peek + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + true + PowerToys.Peek + + + false + PowerToys.Peek + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\$(ProjectName)\ + + + + Use + Level3 + Disabled + true + _DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + MultiThreadedDebug + stdcpplatest + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + pch.h + MultiThreaded + stdcpplatest + + + Windows + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src\modules\peek;%(AdditionalIncludeDirectories) + + + + + + + + + + + Create + Create + pch.h + pch.h + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/peek/peek/resource.h b/src/modules/peek/peek/resource.h new file mode 100644 index 0000000000..67bb929f19 --- /dev/null +++ b/src/modules/peek/peek/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Peek.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys Peek" +#define INTERNAL_NAME "PowerToys.Peek" +#define ORIGINAL_FILENAME "PowerToys.Peek.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/peek/peek/trace.cpp b/src/modules/peek/peek/trace.cpp new file mode 100644 index 0000000000..0dd404b09e --- /dev/null +++ b/src/modules/peek/peek/trace.cpp @@ -0,0 +1,19 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} \ No newline at end of file diff --git a/src/modules/peek/peek/trace.h b/src/modules/peek/peek/trace.h new file mode 100644 index 0000000000..e5c7680cf0 --- /dev/null +++ b/src/modules/peek/peek/trace.h @@ -0,0 +1,8 @@ +#pragma once + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); +}; diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 5c880b59df..d7c102ec88 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -165,6 +165,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow L"modules/FileLocksmith/PowerToys.FileLocksmithExt.dll", L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll", L"modules/Hosts/PowerToys.HostsModuleInterface.dll", + L"modules/Peek/PowerToys.Peek.dll", }; const auto VCM_PATH = L"modules/VideoConference/PowerToys.VideoConferenceModule.dll"; if (const auto mf = LoadLibraryA("mf.dll")) diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs index de29ae6d35..37c6e2b7e4 100644 --- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs +++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs @@ -319,6 +319,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + private bool peek = true; + + [JsonPropertyName("Peek")] + public bool Peek + { + get => peek; + set + { + if (peek != value) + { + LogTelemetryEvent(value); + peek = value; + } + } + } + public string ToJsonString() { return JsonSerializer.Serialize(this); diff --git a/src/settings-ui/Settings.UI.Library/PeekProperties.cs b/src/settings-ui/Settings.UI.Library/PeekProperties.cs new file mode 100644 index 0000000000..f9355817ab --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PeekProperties.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class PeekProperties + { + public PeekProperties() + { + ActivationShortcut = new HotkeySettings(false, true, false, false, 0x20); + } + + public HotkeySettings ActivationShortcut { get; set; } + + public override string ToString() + => JsonSerializer.Serialize(this); + } +} diff --git a/src/settings-ui/Settings.UI.Library/PeekSettings.cs b/src/settings-ui/Settings.UI.Library/PeekSettings.cs new file mode 100644 index 0000000000..7d6b656267 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PeekSettings.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class PeekSettings : BasePTModuleSettings, ISettingsConfig + { + public const string ModuleName = "Peek"; + public const string ModuleVersion = "0.0.1"; + + [JsonPropertyName("properties")] + public PeekProperties Properties { get; set; } + + public PeekSettings() + { + Name = ModuleName; + Version = ModuleVersion; + Properties = new PeekProperties(); + } + + public string GetModuleName() + { + return Name; + } + + public bool UpgradeSettingsConfiguration() + { + return false; + } + } +} diff --git a/src/settings-ui/Settings.UI.Library/ViewModels/PeekViewModel.cs b/src/settings-ui/Settings.UI.Library/ViewModels/PeekViewModel.cs new file mode 100644 index 0000000000..7285e15d17 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/ViewModels/PeekViewModel.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Library.Helpers; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + +namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels +{ + public class PeekViewModel : Observable + { + private bool _isEnabled; + + private GeneralSettings GeneralSettingsConfig { get; set; } + + private readonly ISettingsUtils _settingsUtils; + private readonly PeekSettings _peekSettings; + + private Func SendConfigMSG { get; } + + public PeekViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc) + { + // To obtain the general settings configurations of PowerToys Settings. + if (settingsRepository == null) + { + throw new ArgumentNullException(nameof(settingsRepository)); + } + + GeneralSettingsConfig = settingsRepository.SettingsConfig; + + _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); + if (_settingsUtils.SettingsExists(ColorPickerSettings.ModuleName)) + { + _peekSettings = _settingsUtils.GetSettingsOrDefault(PeekSettings.ModuleName); + } + else + { + _peekSettings = new PeekSettings(); + } + + _isEnabled = GeneralSettingsConfig.Enabled.Peek; + + SendConfigMSG = ipcMSGCallBackFunc; + } + + public bool IsEnabled + { + get => _isEnabled; + set + { + if (_isEnabled != value) + { + _isEnabled = value; + + GeneralSettingsConfig.Enabled.Peek = value; + OnPropertyChanged(nameof(IsEnabled)); + + OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); + SendConfigMSG(outgoing.ToString()); + } + } + } + + public HotkeySettings ActivationShortcut + { + get => _peekSettings.Properties.ActivationShortcut; + set + { + if (_peekSettings.Properties.ActivationShortcut != value) + { + _peekSettings.Properties.ActivationShortcut = value; + OnPropertyChanged(nameof(ActivationShortcut)); + NotifySettingsChanged(); + } + } + } + + private void NotifySettingsChanged() + { + // Using InvariantCulture as this is an IPC message + SendConfigMSG( + string.Format( + CultureInfo.InvariantCulture, + "{{ \"powertoys\": {{ \"{0}\": {1} }} }}", + PeekSettings.ModuleName, + JsonSerializer.Serialize(_peekSettings))); + } + } +} diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPeek.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPeek.png new file mode 100644 index 0000000000..a2939edd84 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPeek.png differ diff --git a/src/settings-ui/Settings.UI/Assets/Modules/Peek.png b/src/settings-ui/Settings.UI/Assets/Modules/Peek.png new file mode 100644 index 0000000000..6d02e22798 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/Modules/Peek.png differ diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index a2f1b3e243..03a8ab9ca6 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2412,6 +2412,20 @@ From there, simply click on one of the supported files in the File Explorer and What's new + + Peek + Product name: Navigation view item name for Peek + + + Peek + + + A quick and easy way to preview files + + + Enable Peek + Peek is a product name, do not loc + Disable round corners when window is snapped diff --git a/src/settings-ui/Settings.UI/Views/PeekPage.xaml b/src/settings-ui/Settings.UI/Views/PeekPage.xaml new file mode 100644 index 0000000000..2c8faf6c53 --- /dev/null +++ b/src/settings-ui/Settings.UI/Views/PeekPage.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Views/PeekPage.xaml.cs b/src/settings-ui/Settings.UI/Views/PeekPage.xaml.cs new file mode 100644 index 0000000000..e1f42df320 --- /dev/null +++ b/src/settings-ui/Settings.UI/Views/PeekPage.xaml.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.ViewModels; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.PowerToys.Settings.UI.Views +{ + public sealed partial class PeekPage : Page + { + private PeekViewModel ViewModel { get; set; } + + public PeekPage() + { + var settingsUtils = new SettingsUtils(); + ViewModel = new PeekViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); + DataContext = ViewModel; + InitializeComponent(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Views/ShellPage.xaml b/src/settings-ui/Settings.UI/Views/ShellPage.xaml index 22e99f75e5..3fbedf9998 100644 --- a/src/settings-ui/Settings.UI/Views/ShellPage.xaml +++ b/src/settings-ui/Settings.UI/Views/ShellPage.xaml @@ -99,6 +99,11 @@ helpers:NavHelper.NavigateTo="views:MouseUtilsPage" Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsMouseUtils.png}" /> + +