From 463ac98eaf6c9ebd03e92f87a3a23d385b61a1d2 Mon Sep 17 00:00:00 2001 From: Clinton Ingram Date: Mon, 16 Jan 2017 15:59:03 -0800 Subject: [PATCH] Initial public commit --- .editorconfig | 5 + .gitignore | 5 + doc/main.md | 303 +++ license | 16 + readme.md | 60 + src/Core/ColorProfileReader.cs | 87 + src/Core/ImageFileInfo.cs | 101 + src/Core/Interpolators.cs | 173 ++ src/Core/LookupTables.cs | 56 + src/Core/MathUtil.cs | 126 ++ src/Core/ProcessImageSettings.cs | 418 ++++ src/GDI/GdiImageProcessor.cs | 165 ++ src/Interop/PropVariant.cs | 373 ++++ src/Interop/StreamAsIStream.cs | 106 + src/Interop/WinCodec.cs | 2689 ++++++++++++++++++++++++++ src/Interop/WinCodecExtensions.cs | 56 + src/Interop/WinCodecSdk.cs | 621 ++++++ src/Interop/license | 31 + src/MagicScaler.csproj | 174 ++ src/Properties/AssemblyInfo.cs | 22 + src/Utilities/CacheHash.cs | 46 + src/Utilities/MiscExtensions.cs | 33 + src/WIC/Generated/Convolver.cs | 1057 ++++++++++ src/WIC/Generated/WicConvolution.cs | 314 +++ src/WIC/KernelMap.cs | 188 ++ src/WIC/MagicImageProcessor.cs | 81 + src/WIC/MagicPlanarImageProcessor.cs | 50 + src/WIC/WicBase.cs | 103 + src/WIC/WicBitmapSource.cs | 57 + src/WIC/WicCodec.cs | 185 ++ src/WIC/WicFormatConverters.cs | 196 ++ src/WIC/WicImageProcessor.cs | 55 + src/WIC/WicMatte.cs | 131 ++ src/WIC/WicPlanarHelpers.cs | 112 ++ src/WIC/WicPlanarSource.cs | 225 +++ src/WIC/WicTransform.cs | 473 +++++ 36 files changed, 8893 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 doc/main.md create mode 100644 license create mode 100644 readme.md create mode 100644 src/Core/ColorProfileReader.cs create mode 100644 src/Core/ImageFileInfo.cs create mode 100644 src/Core/Interpolators.cs create mode 100644 src/Core/LookupTables.cs create mode 100644 src/Core/MathUtil.cs create mode 100644 src/Core/ProcessImageSettings.cs create mode 100644 src/GDI/GdiImageProcessor.cs create mode 100644 src/Interop/PropVariant.cs create mode 100644 src/Interop/StreamAsIStream.cs create mode 100644 src/Interop/WinCodec.cs create mode 100644 src/Interop/WinCodecExtensions.cs create mode 100644 src/Interop/WinCodecSdk.cs create mode 100644 src/Interop/license create mode 100644 src/MagicScaler.csproj create mode 100644 src/Properties/AssemblyInfo.cs create mode 100644 src/Utilities/CacheHash.cs create mode 100644 src/Utilities/MiscExtensions.cs create mode 100644 src/WIC/Generated/Convolver.cs create mode 100644 src/WIC/Generated/WicConvolution.cs create mode 100644 src/WIC/KernelMap.cs create mode 100644 src/WIC/MagicImageProcessor.cs create mode 100644 src/WIC/MagicPlanarImageProcessor.cs create mode 100644 src/WIC/WicBase.cs create mode 100644 src/WIC/WicBitmapSource.cs create mode 100644 src/WIC/WicCodec.cs create mode 100644 src/WIC/WicFormatConverters.cs create mode 100644 src/WIC/WicImageProcessor.cs create mode 100644 src/WIC/WicMatte.cs create mode 100644 src/WIC/WicPlanarHelpers.cs create mode 100644 src/WIC/WicPlanarSource.cs create mode 100644 src/WIC/WicTransform.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ba49e3c2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..47331d8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vs +bin +obj +TestResults +*.sln diff --git a/doc/main.md b/doc/main.md new file mode 100644 index 00000000..d1ee0e3d --- /dev/null +++ b/doc/main.md @@ -0,0 +1,303 @@ +##MagicImageProcessor + +The main MagicScaler image processor. + +###ProcessImage(string, Stream, ProcessImageSettings) + +Accepts a file path for the input image, a stream for the output image, and a ProcessImageSettings object for settings. The output stream must allow Seek and Write. + +###ProcessImage(byte[], Stream, ProcessImageSettings) + +Accepts a byte array for the input image, a stream for the output image, and a ProcessImageSettings object for settings. The output stream must allow Seek and Write. + +###ProcessImage(Stream, Stream, ProcessImageSettings) + +Accepts a stream for the input image, a stream for the output image, and a ProcessImageSettings object for settings. The output stream must allow Seek and Write. The input stream must allow Seek and Read. + +##GdiImageProcessor + +This class is included only for testing/benchmarking purposes. It will be removed in a future version and should not be used for production code. + +##WicImageProcessor + +This class is included only for testing/benchmarking purposes. It will be removed in a future version and should not be used for production code. + +##ProcessImageSettings + +Settings for the ProcessImage operation + +###FrameIndex: int + +The frame number (starting from 0) to read from a multi-frame file, such as a multi-page TIFF or animated GIF. For single-frame images, 0 is the only valid value. + +Default Value: 0 + +###Width: int + +The output image width in pixels. If auto-cropping is enabled, a value of 0 will set the width automatically based on the output height. Width and Height may not both be set to 0. + +Default Value: 0 + +###Height: int + +The output image height in pixels. If auto-cropping is enabled, a value of 0 will set the height automatically based on the output width. Width and Height may not both be set to 0. + +Default Value: 0 + +###Sharpen: bool + +Indicates whether an [unsharp mask](https://en.wikipedia.org/wiki/Unsharp_masking) operation should be performed on the image following the resize. The sharpening settings are controlled by the [UnsharpMask](#UnsharpMask) property. + +Default value: false + +###ResizeMode: CropScaleMode + +A [CropScaleMode](#CropScaleMode) value indicating whether auto-cropping should be performed or whether the resized image may have a different aspect ratio. Auto-cropping is performed only if a [Crop](#Crop) value is not explicitly set. + +Default value: Crop + +###Crop: Rectangle + +A System.Drawing.Rectangle that specifies which part of the input image should be included. If the rectangle is empty and the [ResizeMode](#ResizeMode) is set to `Crop`, the image will be cropped automatically. Points given for this rectangle must be expressed in terms of the input image. + +Default value: Rectangle.Empty + +###Anchor: CropAnchor + +A [CropAnchor](#CropAnchor) value indicating the position of the auto-crop rectangle. Values may be combined to specify a vertical and horizontal position. Ex: `myCrop = CropAnchor.Top | CropAnchor.Left`. + +Default value: CropAnchor.Center + +###SaveFormat: FileFormat + +A [FileFormat](#FileFormat) value indicating the codec used for the output image. A value of `Auto` will choose the output codec based on the input image type. + +Default value: FileFormat.Auto + +###MatteColor: Color + +A System.Drawing.Color value indicating a background color to be applied when processing an input image with transparency. When converting to a file format that does not support transparency (e.g. PNG->JPEG), the background color will be Black unless otherwise specified. When saving as a file format that does support transparency, the transparency will be maintained unless a color is set. + +Default value: Color.Empty + +###HybridMode: HybridScaleMode + +A [HybridScaleMode](#HybridScaleMode) value indicating whether hybrid processing is allowed. Hybrid processing may use the image decoder or another low-quality scaler to shrink an image to an intermediate size before the selected high-quality algorithm is applied to the final resize. This can result in dramatic performance improvements but with a reduction in image quality. + +Default value: HybridScaleMode.FavorQuality + +###BlendingMode: GammaMode + +A [GammaMode](#GammaMode) value indicating whether the scaling algorithm is applied in linear or gamma-corrected colorspace. Linear processing will yield better quality in almost all cases but with a performance cost. + +Default value: GammaMode.Linear + +###MetadataNames: IEnumerable + +A list of metadata policy names or explicit metadata paths to be copied from the input image to the output image. This can be useful for preserving author or copyright EXIF tags in the output image. See the [Windows Photo Metadata Policies](https://msdn.microsoft.com/en-us/library/windows/desktop/ee872003(v=vs.85).aspx) for examples of commonly-used values, or the [Metadata Query Language Overview](https://msdn.microsoft.com/en-us/library/windows/desktop/ee872003(v=vs.85).aspx) for explicit path syntax. + +Default value: null + +###JpegQuality: int + +Sets the quality value passed to the JPEG encoder for the output image. If this value is set to 0, the quality level will be set automatically according to the output image dimensions. Typically, this value should be 80 or greater if set explicitly. + +Default value: 0 + +###JpegSubsampleMode: ChromaSubsampleMode + +A [ChromaSubsampleMode](#ChromaSubsampleMode) value indicating how [chroma subsampling](https://en.wikipedia.org/wiki/Chroma_subsampling) is configured in the JPEG encoder for the output image. If this value is set to `Default`, the chroma subsampling will be set automatically based on the [JpegQuality](#JpegQuality) setting. + +Default value: ChromaSubsampleMode.Default + +###Interpolation: InterpolationSettings + +An [InterpolationSettings](#InterpolationSettings) object specifying details of the sampling algorithm to use for image scaling. If this value is unset, the algorithm will be chosen automatically based on the ratio of input image size to output image size. + +Default value: unset + +###UnsharpMask: UnsharpMaskSettings + +An [UnsharpMaskSettings](#UnsharpMaskSettings) object specifying sharpening settings. If this value is unset, the settings will be chosen automatically based on the ratio of input image size to output image size. + +Default value: unset + +##CropAnchor + +A flags enumeration for specifying auto-crop anchor. + +By default, auto-cropping will maintain the image center by cropping equally from the top, bottom, or sides. If you wish to direct the auto-cropper to focus on another part of the image, you may specify a vertical and horizontal bias using a combination of values. Only one horizontal and one vertical value may be combined. + +* Center +* Top +* Bottom +* Left +* Right + +##CropScaleMode + +An enumeration for specifying auto-crop or auto-size behavior. + +###Crop + +Auto-crop the input image to fit within the given Width and Height while maintaining the aspect ratio of the input image. + +###Max + +Auto-size the output image to a maximum of the values given for Width and Height while maintaining aspect ratio of the input image. + +###Stretch + +Allow the output image Width and Height to change the aspect ratio of the input image. This may result in stretching or distortion of the image. + +###Examples + +Suppose you have an input image with dimensions of 640x480 and you set the Width and Height of the output image to 100x100. +`Crop` will produce an output image of 100x100, preserving the aspect ratio of the input image by cropping from the sides of the image. By default, this will crop evenly from the left and right. You can change that behavior by changing the [Anchor](#CropAnchor) value. +`Max` will produce an output image of 100x75, preserving the aspect ratio of the input image by contstraining the dimensions of the output image. +`Stretch` will produce an output image of 100x100 that is squished horizontally. + +When using `Crop` mode, you may also choose to specify only one of the Width or Height. In this case, and the undefined dimension will be set automatically to preserve the source image's aspect ratio after taking the Crop setting into account. +Again, using a 640x480 input image as an example, you can expect the following: + +Width=100 with the default values Height=0/Crop=Rectangle.Empty will result in an output image of 100x75 with no cropping. +Height=100 with the default values Width=0/Crop=Rectangle.Empty will result in an output image of 133x100 with no cropping. +Width=100/Crop=Rectangle.FromLTRB(0,0,480,480) with the default value Height=0 will result in an output image of 100x100 with the right portion of the image cropped. You can achieve the same result with Width=100/Height=100/Anchor=CropAnchor.Left. + +##HybridScaleMode + +An enumeration for specifying the amount of low-quality scaling performed in high-ratio resizing operations. + +###FavorQuality + +Resize the image to an intermediate size at least 3x the output dimensions with the low-quality scaler. Perform the final resize with the high-quality scaler. + +###FavorSpeed + +Resize the image to an intermediate size at least 2x the output dimensions with the low-quality scaler. Perform the final resize with the high-quality scaler. + +###Turbo + +Resize the image entirely using the low-quality scaler if possible. If not possible, perform the minimal amount of work possible in the high-quality scaler. + +###Off + +Perform the entire resize with the high-quality scaler. This will yield the best quality image but at a performance cost. + +##GammaMode + +An enumeration for specifying the light blending mode used for high-quality scaling operations. + +###Linear + +Perform the high-quality scaling in [linear light](http://web.archive.org/web/20160826144709/http://www.4p8.com/eric.brasseur/gamma.html) colorspace. This will give better results in most cases but at a performance cost. + +###sRGB + +Perform the high-quality scaling in gamma-corrected sRGB colorspace. This will yield output more similar to other scaling software but will be less correct in most cases. + +##ChromaSubsampleMode + +An enumeration for specifying the [chroma subsampling](https://en.wikipedia.org/wiki/Chroma_subsampling) mode used by the JPEG encoder. + +###Default + +Choose the chroma subsampling mode automatically based on the JpegQuality setting + +###Subsample420 + +4:2:0 chroma subsampling + +###Subsample422 + +4:2:2 chroma subsampling + +###Subsample444 + +No chroma subsampling (4:4:4) + +##FileFormat + +An enumeration for specifying the file format (codec) used for the output image. + +###Auto + +Choose the output file format automatically based on the format of the input image + +###Jpeg + +JPEG. Use JpegQuality and JpegSubsampleMode settings to control output. + +###Png + +24-bit or 32-bit PNG, depending on whether or not the input image contains an alpha channel. + +###Png8 + +8-bit indexed PNG + +###Gif + +8-bit indexed GIF + +###Bmp + +24-bit or 32-bit BMP, depending on whether or not the input image contains an alpha channel. + +###Tiff + +Uncompressed 24-bit or 32-bit TIFF, depending on whether or not the input image contains an alpha channel. + +##UnsharpMaskSettings + +A structure for specifying the settings used for the post-resize [sharpening](https://en.wikipedia.org/wiki/Unsharp_masking) of the output image. These settings are designed to function similarly to the Unsharp Mask settings in Photoshop. + +###Amount: int + +The amount of sharpening applied. Technically, this is a percentage applied to the luma difference between the original and blurred images. Typical values are between 25 and 200. + +###Radius: double + +The radius of the gaussian blur used for the unsharp mask. More blurring in the mask yields more sharpening in the final image. Typical values are between 0.3 and 3.0. Larger radius values can have significant performance cost. + +###Threshold + +The minimum difference between the original and blurred images for a pixel to be sharpened. When using larger `Radius` or `Amount` values, a larger `Threshold` value can ensure lines are sharpened while textures are not. Typical values are between 0 and 10. + +##InterpolationSettings + +A structure for specifying the sampling algorithm used by the high-quality scaler. There are a number of well-known algoritms preconfigured as static fields, or you can define your own. + +Preconfigured values include: + +* [NearestNeighbor](http://www.imagemagick.org/Usage/filter/#point) - AKA Point +* [Average](http://www.imagemagick.org/Usage/filter/#box) - AKA Box +* [Linear](http://www.imagemagick.org/Usage/filter/#triangle) - AKA Bilinear/Triangle/Tent +* [Quadratic](http://neildodgson.com/pubs/quad.pdf) - A slightly faster alternative to Catmull-Rom with similar output but a greater chance of artifacts +* [Hermite](http://www.imagemagick.org/Usage/filter/#hermite) - A Cubic filter with B=0, C=0 - Similar to Linear but with slightly smoother output +* [Mitchell](http://www.imagemagick.org/Usage/filter/#mitchell) - AKA Mitchell-Netravali - A Cubic filter with B=1/3, C=1/3 +* [CatmullRom](http://www.imagemagick.org/Usage/filter/#catrom-c) - AKA Catrom - A Cubic filter with B=0, C=0.5 +* [Cubic](http://www.imagemagick.org/Usage/filter/#cubics) - A Cubic filter with B=0, C=1 - Similar to GDI+'s HighQualityBicubic for high-ratio downscaling +* CubicSmoother - A Cubic filter with B=0, C=0.625 and Blur=1.15 - Similar to Photoshop's Bicubic Smoother and GDI+'s HighQualityBicubic when enlarging +* [Lanczos](http://www.imagemagick.org/Usage/filter/#lanczos) - A 3-lobed Lanczos Windowed Sinc filter +* [Spline36](http://www.panotools.org/dersch/interpolator/interpolator.html) - A 3-lobed piece-wise function with a nice balance between smoothness and sharpness + +###WeightingFunction: IInterpolator + +A reference to an object implementing IInterpolator, which specifies the sampling range for the filter and a function to generate the weighting value for a given distance. + +###Blur: double + +A value used to stretch (or compress) the sampling range for the filter. The default and recommnded value is 1. You may use a value greater than 1 to blur or smooth the sampling function. Values less than 1 can cause unpleasant artifacts. + +##IInterpolator + +An interface for defining custom sampling functions. + +###Support: double + +The support radius of the sampling function. The sampling window will be twice this value. + +###GetValue(double) returns double + +The weighting function. This function accepts a distance from the destination sample's center and returns a weight for the sample at that distance. diff --git a/license b/license new file mode 100644 index 00000000..5d781bf2 --- /dev/null +++ b/license @@ -0,0 +1,16 @@ +The Windows Interop portions of this source code are licensed under the +Microsoft Public License (Ms-PL). A copy of the Ms-PL is included in the +Interop folder for reference. + +All other parts of this software, including the complete binary release, are +licensed under the Apache License, Version 2.0 (the "License"). You may not use +this software except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0 + +Copyright (c) 2015-2017 Clinton Ingram + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..8bfb856f --- /dev/null +++ b/readme.md @@ -0,0 +1,60 @@ +MagicScaler +=========== + +MagicScaler brings hig-performance, high-quality image scaling to .NET. + +Requirements +------------ + +Windows 7 SP1 or later* +.NET 4.6 or later + +*Windows 7 and Windows Sever 2008 R2 are supported only with the [Platform Update](https://support.microsoft.com/en-us/kb/2670838) installed. + +Installation +------------ + +MagicScaler is available on [nuget](http://www.nuget.org/packages/PhotoSauce.MagicScaler/) + +``` +PM> Install-Package PhotoSauce.MagicScaler +``` + +Usage +----- + +Basic usage looks something like this: + +```C# +string inPath = @"c:\bigimage.jpg"; +var outStream = new MemoryStream(16384); + +MagicImageProcessor.ProcessImage(inPath, outStream, new ProcessImageSettings { Width = 400 }); + +File.WriteAllBytes(@"c:\smallimage.jpg", outStream.ToArray()); +``` + +See the [documentation page](doc/main.md) for more details. + +Release History +--------------- + +####0.6.0.0 +* Added support for copying metadata from the source image. See the MetadataNames property on ProcessImageSettings. +* Added CubicSmoother preconfigured interpolator. +* Changed Lanczos filter default to 3 lobes. Removed the preconfigured 2-lobe filter and changed the name of Lanczos3 to Lanczos. +* Removed Spline16 interpolator. This was not available as a preconfigured filter but could have been configured manually. +* Removed some unused internal classes. + +####0.5.0.0 +* Initial public release + +Contributing +------------ + +Because MagicScaler is still under active development, I am not accepting unsolicited pull requests at this time. If you find a bug or would like to see a new feature implemented, please open a new issue for further discussion. This will hopefully save any wasted or duplicate efforts. + +License +------- + +See the [license](license) file for details. diff --git a/src/Core/ColorProfileReader.cs b/src/Core/ColorProfileReader.cs new file mode 100644 index 00000000..e77c449f --- /dev/null +++ b/src/Core/ColorProfileReader.cs @@ -0,0 +1,87 @@ +using System; +using System.IO; +using System.Text; +using System.Diagnostics.Contracts; + +namespace PhotoSauce.MagicScaler +{ + //http://www.color.org/specification/ICC1v43_2010-12.pdf + internal class ColorProfileInfo + { + public string CMM { get; private set; } + public string Version { get; private set; } + public string DeviceClass { get; private set; } + public string DataColorSpace { get; private set; } + public string PcsColorSpace { get; private set; } + public string Platform { get; private set; } + public DateTime CreateDate { get; private set; } + public string Manufacturer { get; private set; } + public string Model { get; private set; } + public string Creator { get; private set; } + public bool IsValid { get; private set; } + + public bool IsDisplayRgb => DataColorSpace == "RGB " && DeviceClass == "mntr"; + public bool IsCmyk => DataColorSpace == "CMYK"; + public bool IsStandardSrgb => CMM == "Lino" && Manufacturer == "IEC " && Model == "sRGB"; + + private void init(Stream stm) + { + if (stm.Length < 128) + return; //throw new InvalidDataException("Invalid ICC profile. Header is incomplete."); + + using (var rdr = new BinaryReader(stm, Encoding.ASCII)) + { + uint len = rdr.ReadBigEndianUInt32(); + if (len != rdr.BaseStream.Length) + return; //throw new InvalidDataException("Invalid ICC profile. Internal length doesn't match data length."); + + CMM = new string(rdr.ReadChars(4)); + + var ver = rdr.ReadBytes(4); + IsValid = ver[0] == 2 || ver[0] == 4; + Version = string.Join(".", ver[0], ver[1] >> 4, ver[1] & 0xf, 0); + + DeviceClass = new string(rdr.ReadChars(4)); + + DataColorSpace = new string(rdr.ReadChars(4)); + + PcsColorSpace = new string(rdr.ReadChars(4)); + + CreateDate = new DateTime(rdr.ReadBigEndianUInt16(), rdr.ReadBigEndianUInt16(), rdr.ReadBigEndianUInt16(), rdr.ReadBigEndianUInt16(), rdr.ReadBigEndianUInt16(), rdr.ReadBigEndianUInt16()); + + var acsp = rdr.ReadBytes(4); + + Platform = new string(rdr.ReadChars(4)); + + var flags = rdr.ReadBytes(4); + + Manufacturer = new string(rdr.ReadChars(4)); + + Model = new string(rdr.ReadChars(4)); + + var attributes = rdr.ReadBytes(8); + + var intent = rdr.ReadBytes(4); + + var d50xyz = rdr.ReadBytes(12); + + Creator = new string(rdr.ReadChars(4)); + } + } + + public ColorProfileInfo(byte[] profileData) + { + Contract.Requires(profileData != null, nameof(profileData)); + + using (var ms = new MemoryStream(profileData)) + init(ms); + } + + public ColorProfileInfo(Stream profileData) + { + Contract.Requires(profileData != null, nameof(profileData)); + + init(profileData); + } + } +} \ No newline at end of file diff --git a/src/Core/ImageFileInfo.cs b/src/Core/ImageFileInfo.cs new file mode 100644 index 00000000..e1d239e8 --- /dev/null +++ b/src/Core/ImageFileInfo.cs @@ -0,0 +1,101 @@ +using System; +using System.IO; +using System.Diagnostics.Contracts; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal class ImageFileInfo + { + public struct FrameInfo + { + public int Width { get; private set; } + public int Height { get; private set; } + public bool Rotated90 { get; private set; } + public bool HasAlpha { get; private set; } + + public FrameInfo(int width, int height, bool rotated, bool alpha) + { + Width = width; + Height = height; + Rotated90 = rotated; + HasAlpha = alpha; + } + } + + public long FileSize { get; private set; } + public DateTime FileDate { get; private set; } + public FileFormat ContainerType { get; private set; } + public FrameInfo[] Frames { get; private set; } + + public ImageFileInfo(string imgPath) + { + Contract.Requires(imgPath != null, nameof(imgPath)); + + var fi = new FileInfo(imgPath); + if (!fi.Exists) + throw new ArgumentException("File does not exist"); + + using (var ctx = new WicProcessingContext(new ProcessImageSettings())) + using (var dec = new WicDecoder(imgPath, ctx)) + loadInfo(dec, ctx); + + FileSize = fi.Length; + FileDate = fi.LastWriteTimeUtc; + } + + public ImageFileInfo(byte[] imgBuffer, DateTime lastModified) + { + Contract.Requires(imgBuffer != null, nameof(imgBuffer)); + + using (var ctx = new WicProcessingContext(new ProcessImageSettings())) + using (var dec = new WicDecoder(imgBuffer, ctx)) + loadInfo(dec, ctx); + + FileSize = imgBuffer.Length; + FileDate = lastModified; + } + + public ImageFileInfo(Stream istm, DateTime lastModified) + { + Contract.Requires(istm != null, nameof(istm)); + Contract.Requires(istm.CanSeek && istm.CanRead, "Input Stream must allow Seek and Read"); + Contract.Assert(istm.Length > 0, "Input Stream cannot be empty"); + Contract.Assume(istm.Position < istm.Length, "Input Stream Position is at the end. Did you forget to Seek?"); + + using (var ctx = new WicProcessingContext(new ProcessImageSettings())) + using (var dec = new WicDecoder(istm, ctx)) + loadInfo(dec, ctx); + + FileSize = istm.Length; + FileDate = lastModified; + } + + private void loadInfo(WicDecoder dec, WicProcessingContext ctx) + { + ContainerType = FileFormat.Unknown; + if (ctx.ContainerFormat == Consts.GUID_ContainerFormatJpeg) + ContainerType = FileFormat.Jpeg; + else if (ctx.ContainerFormat == Consts.GUID_ContainerFormatPng) + ContainerType = FileFormat.Png; + else if (ctx.ContainerFormat == Consts.GUID_ContainerFormatGif) + ContainerType = FileFormat.Gif; + else if (ctx.ContainerFormat == Consts.GUID_ContainerFormatBmp) + ContainerType = FileFormat.Bmp; + else if (ctx.ContainerFormat == Consts.GUID_ContainerFormatTiff) + ContainerType = FileFormat.Tiff; + + Frames = new FrameInfo[ctx.ContainerFrameCount]; + for (int i = 0; i < ctx.ContainerFrameCount; i++) + { + ctx.Settings.FrameIndex = i; + using (var frm = new WicFrameReader(dec, ctx)) + using (var met = new WicMetadataReader(frm, basicOnly: true)) + { + Frames[i] = new FrameInfo((int)ctx.Width, (int)ctx.Height, ctx.IsRotated90, ctx.HasAlpha); + } + } + } + } +} \ No newline at end of file diff --git a/src/Core/Interpolators.cs b/src/Core/Interpolators.cs new file mode 100644 index 00000000..1b9563f2 --- /dev/null +++ b/src/Core/Interpolators.cs @@ -0,0 +1,173 @@ +using System; +using System.Diagnostics.Contracts; + +using static System.Math; + +namespace PhotoSauce.MagicScaler.Interpolators +{ + public interface IInterpolator + { + double Support { get; } + double GetValue(double distance); + } + + //http://www.imagemagick.org/Usage/filter/#point + public class PointInterpolator : IInterpolator + { + public double Support => 0.000001; + public double GetValue(double d) => 1.0; + } + + //http://www.imagemagick.org/Usage/filter/#box + public class BoxInterpolator : IInterpolator + { + public double Support => 0.5; + public double GetValue(double d) => d <= 0.5 ? 1.0 : 0.0; + } + + //http://www.imagemagick.org/Usage/filter/#triangle + public class LinearInterpolator : IInterpolator + { + public double Support => 1.0; + public double GetValue(double d) => d < 1.0 ? 1.0 - d : 0.0; + } + + //http://www.imagemagick.org/Usage/filter/#gaussian + public class GaussianInterpolator : IInterpolator + { + private readonly double sigma, support; + private readonly MathUtil.GaussianFactory gauss; + + public GaussianInterpolator(double sigma) + { + Contract.Requires(sigma > 0.0, "Sigma must be greater than 0"); + + this.sigma = sigma; + gauss = new MathUtil.GaussianFactory(sigma); + support = gauss.Support; + } + + public double Support => support; + public double GetValue(double d) => (d < support) ? gauss.GetValue(d) : 0.0; + public override string ToString() => $"{base.ToString()}({sigma})"; + } + + //http://neildodgson.com/pubs/quad.pdf + public class QuadraticInterpolator : IInterpolator + { + private readonly double r; + + public QuadraticInterpolator(double r = 1.0) + { + Contract.Requires(r >= 0.5 && r <= 1.0, "r must be between 0.5 and 1.0"); + + this.r = r; + } + + public double Support => 1.5; + public double GetValue(double d) + { + if (d < 0.5) + return (-2.0 * r) * (d * d) + 0.50 * (r + 1.0); + + if (d < 1.5) + return r * (d * d) + (-2.0 * r - 0.5) * d + 0.75 * (r + 1.0); + + return 0.0; + } + + public override string ToString() => $"{base.ToString()}({r})"; + } + + //http://www.imagemagick.org/Usage/filter/#cubics + public class CubicInterpolator : IInterpolator + { + private readonly double support; + private readonly double b, c, p0, p2, p3, q0, q1, q2, q3; + + public CubicInterpolator(double b = 0.0, double c = 0.5) + { + Contract.Requires(b >= 0.0 && c >= 0.0, "B and C values must be greater than or equal to 0"); + + this.b = b; this.c = c; + support = b == 0.0 && c == 0.0 ? 1.0 : 2.0; + + p0 = ( 6.0 - 2.0 * b ) / 6.0; + p2 = (-18.0 + 12.0 * b + c * 6.0) / 6.0; + p3 = ( 12.0 - 9.0 * b - c * 6.0) / 6.0; + q0 = ( 8.0 * b + c * 24.0) / 6.0; + q1 = ( -12.0 * b - c * 48.0) / 6.0; + q2 = ( 6.0 * b + c * 30.0) / 6.0; + q3 = ( -b - c * 6.0) / 6.0; + } + + public double Support => support; + public double GetValue(double d) + { + if (d < 1.0) + return p0 + d * d * (p2 + d * p3); + + if (support > 1.0 && d < 2.0) + return q0 + d * (q1 + d * (q2 + d * q3)); + + return 0.0; + } + + public override string ToString() => $"{base.ToString()}({b}, {c})"; + } + + //http://en.wikipedia.org/wiki/Lanczos_resampling + public class LanczosInterpolator : IInterpolator + { + private readonly double support; + + public LanczosInterpolator(int lobes = 3) + { + Contract.Requires(lobes > 0, "lobe count must be greater than 0"); + + support = lobes; + } + + public double Support => support; + public double GetValue(double d) + { + if (d == 0.0) + return 1.0; + + if (d < support) + { + d *= PI; + return (support * Sin(d) * Sin(d / support)) / (d * d); + } + + return 0.0; + } + + public override string ToString() => $"{base.ToString()}({support})"; + } + + //http://www.panotools.org/dersch/interpolator/interpolator.html + public class Spline36Interpolator : IInterpolator + { + public double Support => 3.0; + public double GetValue(double d) + { + if (d < 1.0) + return (( 13.0/11.0 * d - 453.0/209.0) * d - 3.0/209.0) * d + 1.0; + + if (d < 2.0) + { + d -= 1.0; + return ((- 6.0/11.0 * d + 270.0/209.0) * d - 156.0/209.0) * d; + } + + if (d < 3.0) + { + d -= 2.0; + return (( 1.0/11.0 * d - 45.0/209.0) * d + 26.0/209.0) * d; + } + + return 0.0; + } + } +} \ No newline at end of file diff --git a/src/Core/LookupTables.cs b/src/Core/LookupTables.cs new file mode 100644 index 00000000..fcb9768c --- /dev/null +++ b/src/Core/LookupTables.cs @@ -0,0 +1,56 @@ +using System; + +using static System.Math; +using static PhotoSauce.MagicScaler.MathUtil; + +namespace PhotoSauce.MagicScaler +{ + internal static class LookupTables + { + private static readonly Lazy alphaTable = new Lazy(() => { + const double ascale = 1d / 255d; + var at = new ushort[256]; + for (int i = 0; i < 256; i++) + at[i] = ScaleToUInt15(i * ascale); + + return at; + }); + + //http://www.w3.org/Graphics/Color/srgb + private static readonly Lazy gammaTable = new Lazy(() => { + var gt = new byte[MaxUint15 + 1]; + for (int i = 0; i < gt.Length; i++) + { + double d = UnscaleToDouble(i); + if (d <= 0.0031308) + d *= 12.92; + else + d = 1.055 * Pow(d, 1.0 / 2.4) - 0.055; + + gt[i] = ClampToByte((int)(d * 255.0 + 0.5)); + } + + return gt; + }); + + private static readonly Lazy inverseGammaTable = new Lazy(() => { + var igt = new ushort[256]; + for (int i = 0; i < igt.Length; i++) + { + double d = i / 255d; + if (d <= 0.04045) + d /= 12.92; + else + d = Pow(((d + 0.055) / 1.055), 2.4); + + igt[i] = ScaleToUInt15(d); + } + + return igt; + }); + + public static ushort[] Alpha => alphaTable.Value; + public static byte[] Gamma => gammaTable.Value; + public static ushort[] InverseGamma => inverseGammaTable.Value; + } +} \ No newline at end of file diff --git a/src/Core/MathUtil.cs b/src/Core/MathUtil.cs new file mode 100644 index 00000000..244608c0 --- /dev/null +++ b/src/Core/MathUtil.cs @@ -0,0 +1,126 @@ +using System.IO; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +using static System.Math; + +namespace PhotoSauce.MagicScaler +{ + internal static class MathUtil + { + private const int ishift = 15; + private const int iscale = 1 << ishift; + private const int imax = (1 << ishift + 1) - 1; + private const int iround = iscale >> 1; + private const double dscale = iscale; + private const double idscale = 1d / dscale; + private const float fscale = iscale; + private const float ifscale = 1f / fscale; + + public const ushort MaxUint15 = imax; + public const double DoubleScale = dscale; + public const int IntScale = iscale; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(this int x, int min, int max) => Min(Max(min, x), max); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(this double x, double min, double max) => Min(Max(min, x), max); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ClampToUInt15(int x) => (ushort)Min(Max(0, x), imax); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte ClampToByte(int x) => (byte)Min(Max(0, x), byte.MaxValue); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ScaleToInt32(double x) => (int)(x * dscale); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ScaleToUInt15(double x) => (ushort)Min(Max(0, (int)(x * dscale)), imax); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double UnscaleToDouble(int x) => x * idscale; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float UnscaleToFloat(int x) => x * ifscale; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int UnscaleToInt32(int x) => x + iround >> ishift; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort UnscaleToUInt15(int x) => (ushort)Min(Max(0, x + iround >> ishift), imax); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte UnscaleToByte(int x) => (byte)Min(Max(0, x + iround >> ishift), byte.MaxValue); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort LumaFromBgr(ushort b, ushort g, ushort r) + { + //http://en.wikipedia.org/wiki/Relative_luminance + const int rY = (ushort)(0.2126 * dscale + 0.5); + const int gY = (ushort)(0.7152 * dscale + 0.5); + const int bY = (ushort)(0.0722 * dscale + 0.5); + + return UnscaleToUInt15(r * rY + g * gY + b * bY); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte LumaFromBgr(byte b, byte g, byte r) + { + //http://www.w3.org/TR/AERT#color-contrast + const int rY = (ushort)(0.299 * dscale + 0.5); + const int gY = (ushort)(0.587 * dscale + 0.5); + const int bY = (ushort)(0.114 * dscale + 0.5); + + return UnscaleToByte(r * rY + g * gY + b * bY); + } + + public static double StandardDeviation(this IEnumerable vals) + { + double mean = vals.Average(); + double variance = vals.Select(val => (val - mean) * (val - mean)).Sum(); + return Sqrt(variance / vals.Count()); + } + + public static uint ReadBigEndianUInt32(this BinaryReader rdr) + { + return (uint)(rdr.ReadByte() << 24 | rdr.ReadByte() << 16 | rdr.ReadByte() << 8 | rdr.ReadByte()); + } + + public static ushort ReadBigEndianUInt16(this BinaryReader rdr) + { + return (ushort)(rdr.ReadByte() << 8 | rdr.ReadByte()); + } + + //http://en.wikipedia.org/wiki/Gaussian_blur + public class GaussianFactory + { + private readonly double sigma; + private readonly double dx; + + public GaussianFactory(double sigma) + { + this.sigma = sigma; + dx = 1d / Sqrt(2d * PI * (sigma * sigma)); + } + + public double Support => sigma * 3d; + + public double GetValue(double d) => dx * Exp(-((d * d) / (2d * (sigma * sigma)))); + + public double[] MakeKernel() + { + int dist = (int)Ceiling(Support); + var kernel = new double[dist * 2 + 1]; + + for (int i = -dist; i <= dist; i++) + kernel[i + dist] = GetValue(i); + + double sum = kernel.Sum(); + return kernel.Select(d => d / sum).ToArray(); + } + } + } +} \ No newline at end of file diff --git a/src/Core/ProcessImageSettings.cs b/src/Core/ProcessImageSettings.cs new file mode 100644 index 00000000..27c713b3 --- /dev/null +++ b/src/Core/ProcessImageSettings.cs @@ -0,0 +1,418 @@ +using System; +using System.IO; +using System.Linq; +using System.Drawing; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +using PhotoSauce.MagicScaler.Interpolators; + +namespace PhotoSauce.MagicScaler +{ + [Flags] + public enum CropAnchor { Center = 0, Top = 1, Bottom = 2, Left = 4, Right = 8 } + public enum CropScaleMode { Crop, Max, Stretch } + public enum HybridScaleMode { FavorQuality, FavorSpeed, Turbo, Off } + public enum GammaMode { Linear, sRGB } + public enum ChromaSubsampleMode { Default = 0, Subsample420 = 1, Subsample422 = 2, Subsample444 = 3 } + public enum FileFormat { Auto, Jpeg, Png, Png8, Gif, Bmp, Tiff, Unknown = Auto } + + public struct UnsharpMaskSettings + { + public static readonly UnsharpMaskSettings None = new UnsharpMaskSettings(); + + public int Amount { get; private set; } + public double Radius { get; private set; } + public byte Threshold { get; private set; } + + public UnsharpMaskSettings(int amount, double radius, byte threshold) + { + Amount = amount; + Radius = radius; + Threshold = threshold; + } + } + + public struct InterpolationSettings + { + public static readonly InterpolationSettings NearestNeighbor = new InterpolationSettings(new PointInterpolator()); + public static readonly InterpolationSettings Average = new InterpolationSettings(new BoxInterpolator()); + public static readonly InterpolationSettings Linear = new InterpolationSettings(new LinearInterpolator()); + public static readonly InterpolationSettings Hermite = new InterpolationSettings(new CubicInterpolator(0, 0)); + public static readonly InterpolationSettings Quadratic = new InterpolationSettings(new QuadraticInterpolator()); + public static readonly InterpolationSettings Mitchell = new InterpolationSettings(new CubicInterpolator(1d/3d, 1d/3d)); + public static readonly InterpolationSettings CatmullRom = new InterpolationSettings(new CubicInterpolator()); + public static readonly InterpolationSettings Cubic = new InterpolationSettings(new CubicInterpolator(0, 1)); + public static readonly InterpolationSettings CubicSmoother = new InterpolationSettings(new CubicInterpolator(0.0, 0.625), 1.15); + public static readonly InterpolationSettings Lanczos = new InterpolationSettings(new LanczosInterpolator()); + public static readonly InterpolationSettings Spline36 = new InterpolationSettings(new Spline36Interpolator()); + + public IInterpolator WeightingFunction { get; private set; } + public double Blur { get; private set; } + + public InterpolationSettings(IInterpolator weighting) : this(weighting, 1d) { } + + public InterpolationSettings(IInterpolator weighting, double blur) + { + Contract.Requires(blur > 0.5 && blur <= 2d, "Blur must be > 0.5 and <= 2"); + + WeightingFunction = weighting; + Blur = blur; + } + } + + public class ProcessImageSettings + { + private static readonly Regex cropExpression = new Regex(@"^(\d+,){3}\d+$", RegexOptions.Compiled); + private static readonly Regex anchorExpression = new Regex(@"^(top|middle|bottom)?\-?(left|center|right)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex subsampleExpression = new Regex(@"^4(20|22|44)$", RegexOptions.Compiled); + private static readonly Regex hexColorExpression = new Regex(@"^[0-9A-Fa-f]{6}$", RegexOptions.Compiled); + + private InterpolationSettings interpolation; + private UnsharpMaskSettings unsharpMask; + private int jpegQuality; + private ChromaSubsampleMode jpegSubsampling; + private ImageFileInfo imageInfo; + + public int FrameIndex { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public bool Sharpen { get; set; } + public CropScaleMode ResizeMode { get; set; } + public Rectangle Crop { get; set; } + public CropAnchor Anchor { get; set; } + public FileFormat SaveFormat { get; set; } + public Color MatteColor { get; set; } + public HybridScaleMode HybridMode { get; set; } + public GammaMode BlendingMode { get; set; } + public IEnumerable MetadataNames { get; set; } + + internal bool Normalized => imageInfo != null; + + public bool IndexedColor => SaveFormat == FileFormat.Png8 || SaveFormat == FileFormat.Gif; + + public double ScaleRatio + { + get + { + return Math.Max(Width > 0 ? (double)Crop.Width / Width : 0d, Height > 0 ? (double)Crop.Height / Height : 0d); + } + } + + public double HybridScaleRatio + { + get + { + if (HybridMode == HybridScaleMode.Off || ScaleRatio < 2d) + return 1d; + + double sr = ScaleRatio / (HybridMode == HybridScaleMode.FavorQuality ? 3 : HybridMode == HybridScaleMode.FavorSpeed ? 2 : 1); + + return Math.Pow(2d, Math.Floor(Math.Log(sr, 2d))); + } + } + + public int JpegQuality + { + get + { + if (jpegQuality > 0) + return jpegQuality; + + int res = Math.Max(Width, Height); + return res <= 160 ? 95 : res <= 320 ? 93 : res <= 480 ? 91 : res <= 640 ? 89 : res <= 1280 ? 87 : res <= 1920 ? 85 : 83; + } + set + { + if (value < 0 || value > 100) + throw new ArgumentException("JpegQuality must be between 0 and 100"); + + jpegQuality = value; + } + } + + public ChromaSubsampleMode JpegSubsampleMode + { + get + { + if (jpegSubsampling != ChromaSubsampleMode.Default) + return jpegSubsampling; + + return JpegQuality >= 95 ? ChromaSubsampleMode.Subsample444 : JpegQuality >= 91 ? ChromaSubsampleMode.Subsample422 : ChromaSubsampleMode.Subsample420; + } + set + { + jpegSubsampling = value; + } + } + + public InterpolationSettings Interpolation + { + get + { + if (interpolation.Blur > 0) + return interpolation; + + var interpolator = InterpolationSettings.Spline36; + double rat = ScaleRatio / HybridScaleRatio; + + if (rat < 0.5) + interpolator = InterpolationSettings.Lanczos; + //else if (rat > 32.0) + // interpolator = InterpolationSettings.Average; + else if (rat > 16.0) + interpolator = InterpolationSettings.Linear; + else if (rat > 8.0) + interpolator = InterpolationSettings.Quadratic; + else if (rat > 4.0) + interpolator = InterpolationSettings.CatmullRom; + + return interpolator; + } + set + { + interpolation = value; + } + } + + public UnsharpMaskSettings UnsharpMask + { + get + { + if (unsharpMask.Amount > 0) + return unsharpMask; + + if (!Sharpen || ScaleRatio == 1.0) + return UnsharpMaskSettings.None; + + if (ScaleRatio < 0.5) + return new UnsharpMaskSettings(40, 1.5, 0); + else if (ScaleRatio < 1.0) + return new UnsharpMaskSettings(30, 1.0, 0); + else if (ScaleRatio < 2.0) + return new UnsharpMaskSettings(25, 0.75, 4); + else if (ScaleRatio < 4.0) + return new UnsharpMaskSettings(75, 0.5, 2); + else if (ScaleRatio < 6.0) + return new UnsharpMaskSettings(50, 0.75, 2); + else if (ScaleRatio < 8.0) + return new UnsharpMaskSettings(100, 0.6, 1); + else if (ScaleRatio < 10.0) + return new UnsharpMaskSettings(125, 0.5, 0); + else + return new UnsharpMaskSettings(150, 0.5, 0); + } + set + { + unsharpMask = value; + } + } + + public static ProcessImageSettings FromDictionary(IDictionary dic) + { + Contract.Requires(dic != null, nameof(dic)); + + var s = new ProcessImageSettings(); + + int x; + s.FrameIndex = Math.Max(int.TryParse(dic.GetValueOrDefault("frame") ?? dic.GetValueOrDefault("page"), out x) ? x : 0, 0); + s.Width = Math.Max(int.TryParse(dic.GetValueOrDefault("width") ?? dic.GetValueOrDefault("w"), out x) ? x : 0, 0); + s.Height = Math.Max(int.TryParse(dic.GetValueOrDefault("height") ?? dic.GetValueOrDefault("h"), out x) ? x : 0, 0); + s.JpegQuality = Math.Max(int.TryParse(dic.GetValueOrDefault("quality"), out x) ? x : 0, 0); + + if (cropExpression.IsMatch(dic.GetValueOrDefault("crop") ?? string.Empty)) + { + string[] ps = dic["crop"].Split(','); + s.Crop = new Rectangle(int.Parse(ps[0]), int.Parse(ps[1]), int.Parse(ps[2]), int.Parse(ps[3])); + } + + foreach (var group in anchorExpression.Match(dic.GetValueOrDefault("anchor") ?? string.Empty).Groups.Cast()) + { + CropAnchor anchor; + if (Enum.TryParse(group.Value, true, out anchor)) + s.Anchor |= anchor; + } + + CropScaleMode mode; + s.ResizeMode = Enum.TryParse(dic.GetValueOrDefault("mode"), true, out mode) ? mode : s.ResizeMode; + + string format = dic.GetValueOrDefault("format"); + if (format != null) + { + FileFormat fmt; + s.SaveFormat = Enum.TryParse(format.ToLower().Replace("jpg", "jpeg"), true, out fmt) ? fmt : s.SaveFormat; + } + + GammaMode bm; + s.BlendingMode = Enum.TryParse(dic.GetValueOrDefault("gamma"), true, out bm) ? bm : s.BlendingMode; + + HybridScaleMode hyb; + s.HybridMode = Enum.TryParse(dic.GetValueOrDefault("hybrid"), true, out hyb) ? hyb : s.HybridMode; + + ChromaSubsampleMode csub; + foreach (var cap in subsampleExpression.Match(dic.GetValueOrDefault("subsample") ?? string.Empty).Captures.Cast()) + s.JpegSubsampleMode = Enum.TryParse(string.Concat("Subsample", cap.Value), true, out csub) ? csub : s.JpegSubsampleMode; + + bool b; + s.Sharpen = bool.TryParse(dic.GetValueOrDefault("sharpen"), out b) ? b : true; + + string color = dic.GetValueOrDefault("bgcolor") ?? dic.GetValueOrDefault("bg"); + if (color != null) + { + if (hexColorExpression.IsMatch(color)) + color = string.Concat('#', color); + + try { s.MatteColor = (Color)(new ColorConverter()).ConvertFromString(color); } catch { } + } + + return s; + } + + internal void Fixup(int inWidth, int inHeight, bool swapDimensions = false) + { + int imgWidth = swapDimensions ? inHeight : inWidth; + int imgHeight = swapDimensions ? inWidth : inHeight; + + var whole = new Rectangle(0, 0, imgWidth, imgHeight); + bool needsCrop = Crop.IsEmpty || !Crop.IntersectsWith(whole); + + if (Width == 0 && Height == 0) + { + ResizeMode = CropScaleMode.Crop; + Crop = needsCrop ? whole : Crop; + Crop.IntersectsWith(whole); + Width = Crop.Width; + Height = Crop.Height; + return; + } + + if (!needsCrop || ResizeMode != CropScaleMode.Crop) + Anchor = CropAnchor.Center; + + int wwin = imgWidth, hwin = imgHeight; + double wrat = Width > 0 ? (double)wwin / Width : (double)hwin / Height; + double hrat = Height > 0 ? (double)hwin / Height : wrat; + + if (needsCrop) + { + if (ResizeMode == CropScaleMode.Crop) + { + double rat = Math.Min(wrat, hrat); + hwin = Height > 0 ? MathUtil.Clamp((int)Math.Ceiling(rat * Height), 1, imgHeight) : hwin; + wwin = Width > 0 ? MathUtil.Clamp((int)Math.Ceiling(rat * Width), 1, imgWidth) : wwin; + + int left = Anchor.HasFlag(CropAnchor.Left) ? 0 : Anchor.HasFlag(CropAnchor.Right) ? (imgWidth - wwin) : ((imgWidth - wwin) / 2); + int top = Anchor.HasFlag(CropAnchor.Top) ? 0 : Anchor.HasFlag(CropAnchor.Bottom) ? (imgHeight - hwin) : ((imgHeight - hwin) / 2); + + Crop = new Rectangle(left, top, wwin, hwin); + + Width = Width > 0 ? Width : Math.Max((int)Math.Round(imgWidth / wrat), 1); + Height = Height > 0 ? Height : Math.Max((int)Math.Round(imgHeight / hrat), 1); + } + else + { + Crop = whole; + + if (ResizeMode == CropScaleMode.Max) + { + double rat = Math.Max(wrat, hrat); + int dim = Math.Max(Width, Height); + + Width = MathUtil.Clamp((int)Math.Round(imgWidth / rat), 1, dim); + Height = MathUtil.Clamp((int)Math.Round(imgHeight / rat), 1, dim); + } + } + } + + Crop.Intersect(whole); + + wrat = Width > 0 ? (double)Crop.Width / Width : (double)Crop.Height / Height; + hrat = Height > 0 ? (double)Crop.Height / Height : wrat; + + Width = Width > 0 ? Width : Math.Max((int)Math.Round(Crop.Width / wrat), 1); + Height = Height > 0 ? Height : Math.Max((int)Math.Round(Crop.Height / hrat), 1); + } + + internal void NormalizeFrom(ImageFileInfo img) + { + Contract.Requires(Width >= 0 && Height >= 0, "Width and Height cannot be negative"); + Contract.Requires(ResizeMode == CropScaleMode.Crop || (Width > 0 && Height > 0), "Width and Height must be > 0 with Stretch and Max Modes"); + Contract.Assert(FrameIndex < img.Frames.Length || img.Frames.Length + FrameIndex < 0, "Invalid FrameIndex"); + + if (FrameIndex < 0) + FrameIndex = img.Frames.Length + FrameIndex; + + var frame = img.Frames[FrameIndex]; + + if (SaveFormat == FileFormat.Auto) + { + if (img.ContainerType == FileFormat.Gif) // Restrict to animated only? + SaveFormat = FileFormat.Gif; + else if (img.ContainerType == FileFormat.Png || (frame.HasAlpha && MatteColor.A < 255)) + SaveFormat = FileFormat.Png; + else + SaveFormat = FileFormat.Jpeg; + } + + if (SaveFormat != FileFormat.Jpeg) + { + JpegSubsampleMode = ChromaSubsampleMode.Default; + JpegQuality = 0; + } + + if (!frame.HasAlpha) + MatteColor = Color.Empty; + + if (!Sharpen) + UnsharpMask = UnsharpMaskSettings.None; + + Fixup(frame.Width, frame.Height, frame.Rotated90); + + imageInfo = img; + } + + internal string GetCacheHash() + { + //Contract.Assert(Normalized, "Hash is only valid for normalized settings."); + + using (var ms = new MemoryStream()) + using (var bw = new BinaryWriter(ms, Encoding.UTF8)) + { + bw.Write(imageInfo?.FileSize ?? 0L); + bw.Write(imageInfo?.FileDate.Ticks ?? 0L); + bw.Write(FrameIndex); + bw.Write(Width); + bw.Write(Height); + bw.Write((int)SaveFormat); + bw.Write((int)BlendingMode); + bw.Write((int)ResizeMode); + bw.Write((int)Anchor); + bw.Write(Crop.X); + bw.Write(Crop.Y); + bw.Write(Crop.Width); + bw.Write(Crop.Height); + bw.Write(MatteColor.A); + bw.Write(MatteColor.R); + bw.Write(MatteColor.G); + bw.Write(MatteColor.B); + bw.Write(HybridScaleRatio); + bw.Write(Interpolation.WeightingFunction.ToString()); + bw.Write(Interpolation.Blur); + bw.Write(UnsharpMask.Amount); + bw.Write(UnsharpMask.Radius); + bw.Write(UnsharpMask.Threshold); + bw.Write(JpegQuality); + bw.Write((int)JpegSubsampleMode); + foreach (string m in MetadataNames ?? Enumerable.Empty()) + bw.Write(m); + bw.Seek(0, SeekOrigin.Begin); + + return CacheHash.Create(bw.BaseStream); + } + } + + internal ProcessImageSettings Clone() => (ProcessImageSettings)MemberwiseClone(); + } +} \ No newline at end of file diff --git a/src/GDI/GdiImageProcessor.cs b/src/GDI/GdiImageProcessor.cs new file mode 100644 index 00000000..998717ff --- /dev/null +++ b/src/GDI/GdiImageProcessor.cs @@ -0,0 +1,165 @@ +using System; +using System.IO; +using System.Linq; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; + +namespace PhotoSauce.MagicScaler +{ + public static class GdiImageProcessor + { + private const int exifOrientationID = 274; + private static readonly ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); + private static readonly ImageCodecInfo jpegCodec = codecs.First(c => c.FormatID == ImageFormat.Jpeg.Guid); + private static readonly ImageCodecInfo tiffCodec = codecs.First(c => c.FormatID == ImageFormat.Tiff.Guid); + + public static void ExifRotate(this Image img) + { + if (!img.PropertyIdList.Contains(exifOrientationID)) + return; + + var prop = img.GetPropertyItem(exifOrientationID); + int val = BitConverter.ToUInt16(prop.Value, 0); + var rot = RotateFlipType.RotateNoneFlipNone; + + if (val == 3 || val == 4) + rot = RotateFlipType.Rotate180FlipNone; + else if (val == 5 || val == 6) + rot = RotateFlipType.Rotate90FlipNone; + else if (val == 7 || val == 8) + rot = RotateFlipType.Rotate270FlipNone; + + if (val == 2 || val == 4 || val == 5 || val == 7) + rot |= RotateFlipType.RotateNoneFlipX; + + if (rot != RotateFlipType.RotateNoneFlipNone) + img.RotateFlip(rot); + } + + public static void ProcessImage(string imgPath, Stream ostm, ProcessImageSettings s) + { + using (var fs = new FileStream(imgPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + ProcessImage(fs, ostm, s); + } + + public static void ProcessImage(byte[] img, Stream ostm, ProcessImageSettings s) + { + using (var ms = new MemoryStream(img, false)) + ProcessImage(ms, ostm, s); + } + + public static void ProcessImage(Stream istm, Stream ostm, ProcessImageSettings s) + { + using (var img = Image.FromStream(istm, true, false)) + { + if (s.FrameIndex > 0) + { + var fd = img.RawFormat.Guid == ImageFormat.Gif.Guid ? FrameDimension.Time : FrameDimension.Page; + if (img.GetFrameCount(fd) > s.FrameIndex) + img.SelectActiveFrame(fd, s.FrameIndex); + else + throw new ArgumentException("Invalid Frame Index"); + } + + img.ExifRotate(); + s.Fixup(img.Width, img.Height); + bool alpha = ((ImageFlags)img.Flags & ImageFlags.HasAlpha) == ImageFlags.HasAlpha; + + var src = img; + var crop = s.Crop; + if (s.HybridScaleRatio > 1d) + { + int intw = (int)Math.Ceiling(img.Width / s.HybridScaleRatio); + int inth = (int)Math.Ceiling(img.Height / s.HybridScaleRatio); + + var bmp = new Bitmap(intw, inth); + using (var gfx = Graphics.FromImage(bmp)) + { + gfx.PixelOffsetMode = PixelOffsetMode.Half; + gfx.CompositingMode = CompositingMode.SourceCopy; + gfx.DrawImage(img, Rectangle.FromLTRB(0, 0, intw, inth), crop.X, crop.Y, crop.Width, crop.Height, GraphicsUnit.Pixel); + } + + src = bmp; + crop = new Rectangle(0, 0, intw, inth); + } + + using (src) + using (var iat = new ImageAttributes()) + using (var bmp = new Bitmap(s.Width, s.Height, alpha ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb)) + using (var gfx = Graphics.FromImage(bmp)) + { + iat.SetWrapMode(WrapMode.TileFlipXY); + gfx.PixelOffsetMode = PixelOffsetMode.Half; + gfx.CompositingMode = CompositingMode.SourceCopy; + gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; + + if (alpha && !s.MatteColor.IsEmpty) + { + gfx.Clear(s.MatteColor); + gfx.CompositingMode = CompositingMode.SourceOver; + gfx.CompositingQuality = CompositingQuality.GammaCorrected; + } + + gfx.DrawImage(src, Rectangle.FromLTRB(0, 0, s.Width, s.Height), crop.X, crop.Y, crop.Width, crop.Height, GraphicsUnit.Pixel, iat); + + switch (s.SaveFormat) + { + case FileFormat.Bmp: + bmp.Save(ostm, ImageFormat.Bmp); + break; + case FileFormat.Tiff: + using (var encoderParams = new EncoderParameters(1)) + using (var param = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionNone)) + { + encoderParams.Param[0] = param; + bmp.Save(ostm, tiffCodec, encoderParams); + } + break; + case FileFormat.Jpeg: + using (var encoderParams = new EncoderParameters(1)) + using (var param = new EncoderParameter(Encoder.Quality, s.JpegQuality)) + { + encoderParams.Param[0] = param; + bmp.Save(ostm, jpegCodec, encoderParams); + } + break; + default: + if (s.IndexedColor) + bmp.Save(ostm, ImageFormat.Gif); + else + bmp.Save(ostm, ImageFormat.Png); + break; + } + } + } + } + + public static Stream CreateBrokenImage(ProcessImageSettings s) + { + s.Fixup(s.Width > 0 ? s.Width : s.Height, s.Height > 0 ? s.Height : s.Width); + if (s.Width <= 0) + s.Height = s.Width = 100; + + using (var bmp = new Bitmap(s.Width, s.Height, PixelFormat.Format24bppRgb)) + using (var gfx = Graphics.FromImage(bmp)) + using (var pen = new Pen(Brushes.White, 1.75f)) + using (var ms = new MemoryStream(8192)) + { + gfx.FillRectangle(Brushes.Gainsboro, Rectangle.FromLTRB(0, 0, s.Width, s.Height)); + gfx.SmoothingMode = SmoothingMode.AntiAlias; + gfx.PixelOffsetMode = PixelOffsetMode.Half; + gfx.CompositingQuality = CompositingQuality.GammaCorrected; + + float l = 0.5f, t = 0.5f, r = s.Width - 0.5f, b = s.Height - 0.5f; + gfx.DrawLines(pen, new PointF[] { new PointF(l, t), new PointF(r, b), new PointF(l, b), new PointF(r, t) }); + gfx.DrawLines(pen, new PointF[] { new PointF(l, b), new PointF(l, t), new PointF(r, t), new PointF(r, b) }); + bmp.Save(ms, ImageFormat.Png); + + ms.Seek(0, SeekOrigin.Begin); + return ms; + } + } + } +} \ No newline at end of file diff --git a/src/Interop/PropVariant.cs b/src/Interop/PropVariant.cs new file mode 100644 index 00000000..4316ed21 --- /dev/null +++ b/src/Interop/PropVariant.cs @@ -0,0 +1,373 @@ +// This file was originally part of WIC Tools, published under the Ms-PL license +// https://web.archive.org/web/20101223145710/http://code.msdn.microsoft.com/wictools/Project/License.aspx +// It has been modified from its original version. Changes copyright Clinton Ingram. + +//---------------------------------------------------------------------------------------- +// THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//---------------------------------------------------------------------------------------- + +using System; +using System.Linq; +using System.Collections; +using System.Runtime.InteropServices; + +namespace PhotoSauce.MagicScaler.Interop +{ + [StructLayout(LayoutKind.Explicit, Size = 16)] + internal struct UnmanagedPropVariant + { + [StructLayout(LayoutKind.Sequential)] + internal struct PropVariantVector + { + public uint count; + public IntPtr ptr; + } + + [FieldOffset(0)] public ushort vt; + + [FieldOffset(2)] public ushort reserved1; + [FieldOffset(4)] public ushort reserved2; + [FieldOffset(6)] public ushort reserved3; + + [FieldOffset(8)] public sbyte sbyteValue; + [FieldOffset(8)] public byte byteValue; + [FieldOffset(8)] public short int16Value; + [FieldOffset(8)] public ushort uint16Value; + [FieldOffset(8)] public int int32Value; + [FieldOffset(8)] public uint uint32Value; + [FieldOffset(8)] public long int64Value; + [FieldOffset(8)] public ulong uint64Value; + [FieldOffset(8)] public float floatValue; + [FieldOffset(8)] public double doubleValue; + + [FieldOffset(8)] public IntPtr pointerValue; + [FieldOffset(8)] public PropVariantVector vectorValue; + } + + internal sealed class PropVariant : IEquatable + { + public object Value { get; private set; } + public PropVariantMarshalType MarshalType { get; private set; } + + public PropVariant() : this (null) { } + + public PropVariant(object value) : this(value, PropVariantMarshalType.Automatic) { } + + public PropVariant(object value, PropVariantMarshalType marshalType) + { + MarshalType = marshalType; + Value = value; + + if ((value is Array) && value.GetType().Equals(typeof(object[]))) + { + var objectArray = (object[])value; + var typedArray = Array.CreateInstance(objectArray[0].GetType(), objectArray.Length); + Array.Copy(objectArray, typedArray, objectArray.Length); + + Value = typedArray; + } + + GetUnmanagedType(); + } + + private VarEnum GetUnmanagedType() + { + if (Value == null) return VarEnum.VT_EMPTY; + + var type = Value.GetType(); + if ((MarshalType == PropVariantMarshalType.Blob ) && type.Equals(typeof(byte []))) return VarEnum.VT_BLOB; + else if ((MarshalType == PropVariantMarshalType.Ascii) && type.Equals(typeof(string ))) return VarEnum.VT_LPSTR; + else if ((MarshalType == PropVariantMarshalType.Ascii) && type.Equals(typeof(string[]))) return VarEnum.VT_LPSTR | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(sbyte ))) return VarEnum.VT_I1; + else if (type.Equals(typeof(sbyte []))) return VarEnum.VT_I1 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(byte ))) return VarEnum.VT_UI1; + else if (type.Equals(typeof(byte []))) return VarEnum.VT_UI1 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(short ))) return VarEnum.VT_I2; + else if (type.Equals(typeof(short []))) return VarEnum.VT_I2 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(ushort ))) return VarEnum.VT_UI2; + else if (type.Equals(typeof(ushort[]))) return VarEnum.VT_UI2 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(int ))) return VarEnum.VT_I4; + else if (type.Equals(typeof(int []))) return VarEnum.VT_I4 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(uint ))) return VarEnum.VT_UI4; + else if (type.Equals(typeof(uint []))) return VarEnum.VT_UI4 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(long ))) return VarEnum.VT_I8; + else if (type.Equals(typeof(long []))) return VarEnum.VT_I8 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(ulong ))) return VarEnum.VT_UI8; + else if (type.Equals(typeof(ulong []))) return VarEnum.VT_UI8 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(float ))) return VarEnum.VT_R4; + else if (type.Equals(typeof(float []))) return VarEnum.VT_R4 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(double ))) return VarEnum.VT_R8; + else if (type.Equals(typeof(double[]))) return VarEnum.VT_R8 | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(bool ))) return VarEnum.VT_BOOL; + else if (type.Equals(typeof(bool []))) return VarEnum.VT_BOOL | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(string ))) return VarEnum.VT_LPWSTR; + else if (type.Equals(typeof(string[]))) return VarEnum.VT_LPWSTR | VarEnum.VT_VECTOR; + else if (type.Equals(typeof(DateTime))) return VarEnum.VT_FILETIME; + else if (type.IsCOMObject) return VarEnum.VT_UNKNOWN; + + throw new NotImplementedException(); + } + + public VarEnum UnmanagedType => GetUnmanagedType(); + + public bool Equals(PropVariant other) + { + if (ReferenceEquals(other, null)) + return false; + + if ((Value is Array) != (other.Value is Array)) + return false; + + if ((Value is Array)) + return ((IEnumerable)Value).Cast().SequenceEqual(((IEnumerable)other.Value).Cast()); + + return ReferenceEquals(Value, other.Value) || !ReferenceEquals(Value, null) && Value.Equals(other.Value); + } + + public static bool operator ==(PropVariant pv1, PropVariant pv2) => ReferenceEquals(pv1, pv2) || !ReferenceEquals(pv1, null) && pv1.Equals(pv2); + public static bool operator !=(PropVariant pv1, PropVariant pv2) => !(pv1 == pv2); + + public override bool Equals(object o) => Equals(o as PropVariant); + public override int GetHashCode() => Value == null ? 0 : Value.GetHashCode(); + public override string ToString() => $"{(UnmanagedType & ~VarEnum.VT_VECTOR)}: {(Value is Array ? string.Join(" ", (Array)Value) : Value)}"; + + internal enum PropVariantMarshalType { Automatic, Ascii, Blob } + + internal sealed class Marshaler : ICustomMarshaler + { + [DllImport("kernel32", EntryPoint="RtlZeroMemory", SetLastError=false)] + private extern static void ZeroMemory(IntPtr dst, int length); + + [DllImport("ole32", PreserveSig = false)] + private extern static void PropVariantClear([In, Out] IntPtr pvar); + + private static T[] ToArrayOf(IntPtr p, int size) where T : struct + { + var res = new T[size]; + for (int i = 0; i < size; i++) + res[i] = Marshal.PtrToStructure(new IntPtr(p.ToInt64() + i * Marshal.SizeOf(typeof(T)))); + + return res; + } + + public static ICustomMarshaler GetInstance(string str) => new Marshaler(); + + private PropVariant pv; + + private IntPtr AllocatePropVariant() + { + int size = Marshal.SizeOf(typeof(UnmanagedPropVariant)); + var pNativeData = Marshal.AllocCoTaskMem(size); + ZeroMemory(pNativeData, size); + + return pNativeData; + } + + public void CleanUpManagedData(object obj) { } + + public void CleanUpNativeData(IntPtr pNativeData) + { + PropVariantClear(pNativeData); + Marshal.FreeCoTaskMem(pNativeData); + } + + public int GetNativeDataSize() => -1; + + public IntPtr MarshalManagedToNative(object o) + { + if (o == null) + return IntPtr.Zero; + + var marshalType = PropVariantMarshalType.Automatic; + if (o is PropVariant) + { + pv = (PropVariant)o; + o = pv.Value; + marshalType = pv.MarshalType; + } + + var pNativeData = AllocatePropVariant(); + if (o == null) + return pNativeData; + + var type = o.GetType(); + if (!(o is Array)) + { + if (marshalType == PropVariantMarshalType.Ascii) + { + var upv = new UnmanagedPropVariant(); + upv.vt = (ushort)VarEnum.VT_LPSTR; + upv.pointerValue = Marshal.StringToCoTaskMemAnsi((string)o); + Marshal.StructureToPtr(upv, pNativeData, false); + } + else if (o is string) + { + var upv = new UnmanagedPropVariant(); + upv.vt = (ushort)VarEnum.VT_LPWSTR; + upv.pointerValue = Marshal.StringToCoTaskMemUni((string)o); + Marshal.StructureToPtr(upv, pNativeData, false); + } + else if (o is DateTime) + { + var upv = new UnmanagedPropVariant(); + upv.vt = (ushort)VarEnum.VT_FILETIME; + upv.int64Value = ((DateTime)o).ToFileTimeUtc(); + Marshal.StructureToPtr(upv, pNativeData, false); + } + else + { + Marshal.GetNativeVariantForObject(o, pNativeData); + } + } + else if ((type.Equals(typeof(byte []))) || (type.Equals(typeof(sbyte []))) || + (type.Equals(typeof(short[]))) || (type.Equals(typeof(ushort[]))) || + (type.Equals(typeof(int []))) || (type.Equals(typeof(uint []))) || + (type.Equals(typeof(long []))) || (type.Equals(typeof(ulong []))) || + (type.Equals(typeof(float[]))) || (type.Equals(typeof(double[])))) + { + var a = (Array)o; + int count = a.Length; + int elementSize = Marshal.SizeOf(type.GetElementType()); + var pNativeBuffer = Marshal.AllocCoTaskMem(elementSize * count); + + var gch = GCHandle.Alloc(a, GCHandleType.Pinned); + for (int i = 0; i < count; i++) + { + var pNativeValue = Marshal.UnsafeAddrOfPinnedArrayElement(a, i); + for (int j = 0; j < elementSize; j++) + { + byte value = Marshal.ReadByte(pNativeValue, j); + Marshal.WriteByte(pNativeBuffer, elementSize * i + j, value); + } + } + gch.Free(); + + var upv = new UnmanagedPropVariant(); + upv.vectorValue.count = (uint)count; + upv.vectorValue.ptr = pNativeBuffer; + upv.vt = (ushort)(pv ?? new PropVariant(o)).UnmanagedType; + + Marshal.StructureToPtr(upv, pNativeData, false); + } + else if (type.Equals(typeof(string[]))) + { + int count = ((Array)o).Length; + var pNativeBuffer = Marshal.AllocCoTaskMem(IntPtr.Size * count); + + for (int i = 0; i < count; i++) + { + var strPtr = Marshal.StringToCoTaskMemUni(((string[])o)[i]); + Marshal.WriteIntPtr(pNativeBuffer, IntPtr.Size * i, strPtr); + } + + var upv = new UnmanagedPropVariant(); + upv.vectorValue.count = (uint)count; + upv.vectorValue.ptr = pNativeBuffer; + upv.vt = (ushort)(pv ?? new PropVariant(o)).UnmanagedType; + + Marshal.StructureToPtr(upv, pNativeData, false); + } + else + { + throw new NotImplementedException(); + } + + return pNativeData; + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + if ((pNativeData == IntPtr.Zero) || (pv == null)) + return null; + + var upv = Marshal.PtrToStructure(pNativeData); + pv.MarshalType = PropVariantMarshalType.Automatic; + + if (((upv.vt & (ushort)VarEnum.VT_VECTOR) == 0) && (upv.vt != (ushort)VarEnum.VT_BLOB)) + { + switch ((VarEnum)upv.vt) + { + case VarEnum.VT_EMPTY: + pv.Value = null; + break; + case VarEnum.VT_CLSID: + if (upv.vectorValue.ptr != IntPtr.Zero) + pv.Value = Marshal.PtrToStructure(upv.vectorValue.ptr); + break; + case VarEnum.VT_LPSTR: + pv.MarshalType = PropVariantMarshalType.Ascii; + pv.Value = Marshal.PtrToStringAnsi(upv.pointerValue); + break; + case VarEnum.VT_LPWSTR: + pv.Value = Marshal.PtrToStringUni(upv.pointerValue); + break; + case VarEnum.VT_FILETIME: + pv.Value = DateTime.FromFileTimeUtc(upv.int64Value); + break; + default: + pv.Value = Marshal.GetObjectForNativeVariant(pNativeData); + break; + } + } + else + { + var elementVt = (VarEnum)(upv.vt & ~(ushort)VarEnum.VT_VECTOR); + int count = (int)upv.vectorValue.count; + + switch (elementVt) + { + case VarEnum.VT_I1: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_UI1: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_I2: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_UI2: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_I4: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_UI4: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_I8: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_UI8: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_R4: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_R8: + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); + break; + case VarEnum.VT_BLOB: + pv.MarshalType = PropVariantMarshalType.Blob; + pv.Value = ToArrayOf(upv.vectorValue.ptr, count); ; + break; + case VarEnum.VT_LPSTR: + pv.MarshalType = PropVariantMarshalType.Ascii; + pv.Value = Array.ConvertAll(ToArrayOf(upv.vectorValue.ptr, count), Marshal.PtrToStringAnsi); + break; + case VarEnum.VT_LPWSTR: + pv.Value = Array.ConvertAll(ToArrayOf(upv.vectorValue.ptr, count), Marshal.PtrToStringUni); + break; + default: throw new NotImplementedException(); + } + } + + return null; + } + } + } +} diff --git a/src/Interop/StreamAsIStream.cs b/src/Interop/StreamAsIStream.cs new file mode 100644 index 00000000..f030b5f9 --- /dev/null +++ b/src/Interop/StreamAsIStream.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG; + +namespace PhotoSauce.MagicScaler.Interop +{ + internal static class StreamAsIStreamExtension + { + private class StreamAsIStream : IStream + { + private readonly Stream stream; + + internal StreamAsIStream(Stream backingStream) + { + stream = backingStream; + } + + void IStream.Read(byte[] pv, int cb, IntPtr pcbRead) + { + Marshal.WriteInt32(pcbRead, stream.Read(pv, 0, cb)); + } + + void IStream.Write(byte[] pv, int cb, IntPtr pcbWritten) + { + stream.Write(pv, 0, cb); + + if (pcbWritten != IntPtr.Zero) + Marshal.WriteInt32(pcbWritten, cb); + } + + void IStream.Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) + { + long pos = stream.Seek(dlibMove, (SeekOrigin)dwOrigin); + + if (plibNewPosition != IntPtr.Zero) + Marshal.WriteInt64(plibNewPosition, pos); + } + + void IStream.SetSize(long libNewSize) + { + stream.SetLength(libNewSize); + } + + void IStream.Commit(int grfCommitFlags) + { + stream.Flush(); + } + + void IStream.Stat(out STATSTG pstatstg, int grfStatFlag) + { + pstatstg = new STATSTG { cbSize = stream.Length, type = 2 /*STGTY_STREAM*/ }; + } + + void IStream.CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) + { + const int buffSize = 81920; + byte[] buff = new byte[buffSize]; + int read = 0, totalRead = 0; + + do + { + int toRead = Math.Min((int)cb - totalRead, buffSize); + read = stream.Read(buff, 0, toRead); + stream.Write(buff, 0, read); + totalRead += read; + } while (read > 0); + + if (pcbRead != IntPtr.Zero) + Marshal.WriteInt64(pcbRead, totalRead); + + if (pcbWritten != IntPtr.Zero) + Marshal.WriteInt64(pcbWritten, totalRead); + } + + void IStream.Clone(out IStream ppstm) + { + throw new NotImplementedException(); + } + + void IStream.Revert() + { + throw new NotImplementedException(); + } + + void IStream.LockRegion(long libOffset, long cb, int dwLockType) + { + throw new NotImplementedException(); + } + + void IStream.UnlockRegion(long libOffset, long cb, int dwLockType) + { + throw new NotImplementedException(); + } + } + + public static IStream AsIStream(this Stream s) + { + Contract.Requires(s != null, nameof(s)); + + return new StreamAsIStream(s); + } + } +} diff --git a/src/Interop/WinCodec.cs b/src/Interop/WinCodec.cs new file mode 100644 index 00000000..6c374ec7 --- /dev/null +++ b/src/Interop/WinCodec.cs @@ -0,0 +1,2689 @@ +// This file was originally part of WIC Tools, published under the Ms-PL license +// https://web.archive.org/web/20101223145710/http://code.msdn.microsoft.com/wictools/Project/License.aspx +// It has been modified from its original version. Changes copyright Clinton Ingram. + +//---------------------------------------------------------------------------------------- +// THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//---------------------------------------------------------------------------------------- + +using System; +using System.Text; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG; + +namespace PhotoSauce.MagicScaler.Interop +{ + internal enum WinCodecError + { + WINCODEC_ERR_GENERIC_ERROR = unchecked((int)0x80004005), + WINCODEC_ERR_INVALIDPARAMETER = unchecked((int)0x80070057), + WINCODEC_ERR_OUTOFMEMORY = unchecked((int)0x8007000E), + WINCODEC_ERR_NOTIMPLEMENTED = unchecked((int)0x80004001), + WINCODEC_ERR_ABORTED = unchecked((int)0x80004004), + WINCODEC_ERR_ACCESSDENIED = unchecked((int)0x80070005), + WINCODEC_ERR_VALUEOVERFLOW = unchecked((int)0x80070216), + + WINCODEC_ERR_WRONGSTATE = unchecked((int)0x88982f04), + WINCODEC_ERR_VALUEOUTOFRANGE = unchecked((int)0x88982f05), + WINCODEC_ERR_UNKNOWNIMAGEFORMAT = unchecked((int)0x88982f07), + WINCODEC_ERR_UNSUPPORTEDVERSION = unchecked((int)0x88982f0B), + WINCODEC_ERR_NOTINITIALIZED = unchecked((int)0x88982f0C), + WINCODEC_ERR_ALREADYLOCKED = unchecked((int)0x88982f0D), + + WINCODEC_ERR_PROPERTYNOTFOUND = unchecked((int)0x88982f40), + WINCODEC_ERR_PROPERTYNOTSUPPORTED = unchecked((int)0x88982f41), + WINCODEC_ERR_PROPERTYSIZE = unchecked((int)0x88982f42), + + WINCODEC_ERR_CODECPRESENT = unchecked((int)0x88982f43), + WINCODEC_ERR_CODECNOTHUMBNAIL = unchecked((int)0x88982f44), + WINCODEC_ERR_PALETTEUNAVAILABLE = unchecked((int)0x88982f45), + WINCODEC_ERR_CODECTOOMANYSCANLINES = unchecked((int)0x88982f46), + WINCODEC_ERR_INTERNALERROR = unchecked((int)0x88982f48), + WINCODEC_ERR_SOURCERECTDOESNOTMATCHDIMENSIONS = unchecked((int)0x88982f49), + WINCODEC_ERR_COMPONENTNOTFOUND = unchecked((int)0x88982f50), + WINCODEC_ERR_IMAGESIZEOUTOFRANGE = unchecked((int)0x88982f51), + WINCODEC_ERR_TOOMUCHMETADATA = unchecked((int)0x88982f52), + + WINCODEC_ERR_BADIMAGE = unchecked((int)0x88982f60), + WINCODEC_ERR_BADHEADER = unchecked((int)0x88982f61), + WINCODEC_ERR_FRAMEMISSING = unchecked((int)0x88982f62), + WINCODEC_ERR_BADMETADATAHEADER = unchecked((int)0x88982f63), + WINCODEC_ERR_BADSTREAMDATA = unchecked((int)0x88982f70), + WINCODEC_ERR_STREAMWRITE = unchecked((int)0x88982f71), + WINCODEC_ERR_STREAMREAD = unchecked((int)0x88982f72), + WINCODEC_ERR_STREAMNOTAVAILABLE = unchecked((int)0x88982f73), + WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT = unchecked((int)0x88982f80), + WINCODEC_ERR_UNSUPPORTEDOPERATION = unchecked((int)0x88982f81), + + WINCODEC_ERR_INVALIDREGISTRATION = unchecked((int)0x88982f8A), + WINCODEC_ERR_COMPONENTINITIALIZEFAILURE = unchecked((int)0x88982f8B), + WINCODEC_ERR_INSUFFICIENTBUFFER = unchecked((int)0x88982f8C), + WINCODEC_ERR_DUPLICATEMETADATAPRESENT = unchecked((int)0x88982f8D), + WINCODEC_ERR_PROPERTYUNEXPECTEDTYPE = unchecked((int)0x88982f8E), + WINCODEC_ERR_UNEXPECTEDSIZE = unchecked((int)0x88982f8F), + + WINCODEC_ERR_INVALIDQUERYREQUEST = unchecked((int)0x88982f90), + WINCODEC_ERR_UNEXPECTEDMETADATATYPE = unchecked((int)0x88982f91), + WINCODEC_ERR_REQUESTONLYVALIDATMETADATAROOT = unchecked((int)0x88982f92), + WINCODEC_ERR_INVALIDQUERYCHARACTER = unchecked((int)0x88982f93), + WINCODEC_ERR_WIN32ERROR = unchecked((int)0x88982f94), + WINCODEC_ERR_INVALIDPROGRESSIVELEVEL = unchecked((int)0x88982f95) + } + + [Flags] + internal enum GenericAccessRights : uint + { + GENERIC_READ = 0x80000000, + GENERIC_WRITE = 0x40000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_ALL = 0x10000000, + + GENERIC_READ_WRITE = GENERIC_READ | GENERIC_WRITE + } + + [ComImport, Guid("00000100-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IEnumUnknown + { + [PreserveSig] + int Next( + uint celt, + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.IUnknown)] + object[] rgelt, + ref uint celtFetched + ); + + [PreserveSig] + int Skip( + uint celt + ); + + [PreserveSig] + int Reset(); + + [PreserveSig] + int Clone( + out IEnumUnknown enumUnknown + ); + } + + internal enum CLIPFORMAT : short + { + CF_TEXT = 1, + CF_BITMAP = 2, + CF_METAFILEPICT = 3, + CF_SYLK = 4, + CF_DIF = 5, + CF_TIFF = 6, + CF_OEMTEXT = 7, + CF_DIB = 8, + CF_PALETTE = 9, + CF_PENDATA = 10, + CF_RIFF = 11, + CF_WAVE = 12, + CF_UNICODETEXT = 13, + CF_ENHMETAFILE = 14, + CF_HDROP = 15, + CF_LOCALE = 16, + CF_MAX = 17, + CF_OWNERDISPLAY = 0x80, + CF_DSPTEXT = 0x81, + CF_DSPBITMAP = 0x82, + CF_DSPMETAFILEPICT = 0x83, + CF_DSPENHMETAFILE = 0x8E, + } + + internal enum PROPBAG2_TYPE + { + PROPBAG2_TYPE_UNDEFINED = 0, + PROPBAG2_TYPE_DATA = 1, // Value is simple data + PROPBAG2_TYPE_URL = 2, // Value is a URL reference + PROPBAG2_TYPE_OBJECT = 3, // Value is an object + PROPBAG2_TYPE_STREAM = 4, // Value is a stream + PROPBAG2_TYPE_STORAGE = 5, // Value is a storage + PROPBAG2_TYPE_MONIKER = 6 // Value is a moniker + } + + internal struct PROPBAG2 + { + public uint dwType; // Property type (from PROPBAG2_TYPE) + short _vt; // VARIANT property type + public CLIPFORMAT cfType; // Clipboard format (aka MIME-type) + public uint dwHint; // Property name hint + [MarshalAs(UnmanagedType.LPWStr)] + public string pstrName; // Property name + public Guid clsid; // CLSID (for PROPBAG2_TYPE_OBJECT) + + public VarEnum vt + { + get { return (VarEnum)_vt; } + set { _vt = (short)value; } + } + } + + [ComImport, Guid("3127CA40-446E-11CE-8135-00AA004BB851"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IErrorLog + { + void AddError( + [MarshalAs(UnmanagedType.LPWStr)] + string pszPropName, + [In] + ref EXCEPINFO pExcepInfo + ); + } + + [ComImport, Guid("22F55882-280B-11d0-A8A9-00A0C90C2004"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IPropertyBag2 + { + void Read( + uint cProperties, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + PROPBAG2[] pPropBag, + IErrorLog pErrLog, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + object[] pvarValue, + [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Error, SizeParamIndex = 0)] + int[] phrError + ); + + void Write( + uint cProperties, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + PROPBAG2[] pPropBag, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + object[] pvarValue + ); + + uint CountProperties(); + + void GetPropertyInfo( + uint iProperty, + uint cProperties, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + PROPBAG2[] pPropBag, + out uint pcProperties + ); + + void LoadObject( + [MarshalAs(UnmanagedType.LPWStr)] + string pstrName, + uint dwHint, + [MarshalAs(UnmanagedType.IUnknown)] + object pUnkObject, + IErrorLog pErrLog + ); + } + + internal static class Consts + { + public const int WINCODEC_SDK_VERSION = 0x0237; + + public static readonly Guid CATID_WICBitmapDecoders = new Guid(0x7ed96837, 0x96f0, 0x4812, 0xb2, 0x11, 0xf1, 0x3c, 0x24, 0x11, 0x7e, 0xd3); + public static readonly Guid CATID_WICBitmapEncoders = new Guid(0xac757296, 0x3522, 0x4e11, 0x98, 0x62, 0xc1, 0x7b, 0xe5, 0xa1, 0x76, 0x7e); + public static readonly Guid CATID_WICFormatConverters = new Guid(0x7835eae8, 0xbf14, 0x49d1, 0x93, 0xce, 0x53, 0x3a, 0x40, 0x7b, 0x22, 0x48); + public static readonly Guid CATID_WICMetadataReader = new Guid(0x05af94d8, 0x7174, 0x4cd2, 0xbe, 0x4a, 0x41, 0x24, 0xb8, 0x0e, 0xe4, 0xb8); + public static readonly Guid CATID_WICMetadataWriter = new Guid(0xabe3b9a4, 0x257d, 0x4b97, 0xbd, 0x1a, 0x29, 0x4a, 0xf4, 0x96, 0x22, 0x2e); + public static readonly Guid CATID_WICPixelFormats = new Guid(0x2b46e70f, 0xcda7, 0x473e, 0x89, 0xf6, 0xdc, 0x96, 0x30, 0xa2, 0x39, 0x0b); + + public static readonly Guid CLSID_WIC8BIMIPTCMetadataReader = new Guid(0x0010668c, 0x0801, 0x4da6, 0xa4, 0xa4, 0x82, 0x65, 0x22, 0xb6, 0xd2, 0x8f); + public static readonly Guid CLSID_WIC8BIMIPTCMetadataWriter = new Guid(0x00108226, 0xee41, 0x44a2, 0x9e, 0x9c, 0x4b, 0xe4, 0xd5, 0xb1, 0xd2, 0xcd); + public static readonly Guid CLSID_WIC8BIMResolutionInfoMetadataReader = new Guid(0x5805137A, 0xE348, 0x4F7C, 0xB3, 0xCC, 0x6D, 0xB9, 0x96, 0x5A, 0x05, 0x99); + public static readonly Guid CLSID_WIC8BIMResolutionInfoMetadataWriter = new Guid(0x4ff2fe0e, 0xe74a, 0x4b71, 0x98, 0xc4, 0xab, 0x7d, 0xc1, 0x67, 0x07, 0xba); + public static readonly Guid CLSID_WICAPEMetadataReader = new Guid(0x1767B93A, 0xB021, 0x44EA, 0x92, 0x0F, 0x86, 0x3C, 0x11, 0xF4, 0xF7, 0x68); + public static readonly Guid CLSID_WICAPEMetadataWriter = new Guid(0xBD6EDFCA, 0x2890, 0x482F, 0xB2, 0x33, 0x8D, 0x73, 0x39, 0xA1, 0xCF, 0x8D); + public static readonly Guid CLSID_WICApp0MetadataReader = new Guid(0x43324B33, 0xA78F, 0x480f, 0x91, 0x11, 0x96, 0x38, 0xAA, 0xCC, 0xC8, 0x32); + public static readonly Guid CLSID_WICApp0MetadataWriter = new Guid(0xF3C633A2, 0x46C8, 0x498e, 0x8F, 0xBB, 0xCC, 0x6F, 0x72, 0x1B, 0xBC, 0xDE); + public static readonly Guid CLSID_WICApp13MetadataReader = new Guid(0xAA7E3C50, 0x864C, 0x4604, 0xBC, 0x04, 0x8B, 0x0B, 0x76, 0xE6, 0x37, 0xF6); + public static readonly Guid CLSID_WICApp13MetadataWriter = new Guid(0x7B19A919, 0xA9D6, 0x49E5, 0xBD, 0x45, 0x02, 0xC3, 0x4E, 0x4E, 0x4C, 0xD5); + public static readonly Guid CLSID_WICApp1MetadataReader = new Guid(0xdde33513, 0x774e, 0x4bcd, 0xae, 0x79, 0x02, 0xf4, 0xad, 0xfe, 0x62, 0xfc); + public static readonly Guid CLSID_WICApp1MetadataWriter = new Guid(0xee366069, 0x1832, 0x420f, 0xb3, 0x81, 0x04, 0x79, 0xad, 0x06, 0x6f, 0x19); + public static readonly Guid CLSID_WICBmpDecoder = new Guid(0x6b462062, 0x7cbf, 0x400d, 0x9f, 0xdb, 0x81, 0x3d, 0xd1, 0x0f, 0x27, 0x78); + public static readonly Guid CLSID_WICBmpEncoder = new Guid(0x69be8bb4, 0xd66d, 0x47c8, 0x86, 0x5a, 0xed, 0x15, 0x89, 0x43, 0x37, 0x82); + public static readonly Guid CLSID_WICExifMetadataReader = new Guid(0xd9403860, 0x297f, 0x4a49, 0xbf, 0x9b, 0x77, 0x89, 0x81, 0x50, 0xa4, 0x42); + public static readonly Guid CLSID_WICExifMetadataWriter = new Guid(0xc9a14cda, 0xc339, 0x460b, 0x90, 0x78, 0xd4, 0xde, 0xbc, 0xfa, 0xbe, 0x91); + public static readonly Guid CLSID_WICDefaultFormatConverter = new Guid(0x1a3f11dc, 0xb514, 0x4b17, 0x8c, 0x5f, 0x21, 0x54, 0x51, 0x38, 0x52, 0xf1); + public static readonly Guid CLSID_WICFormatConverterHighColor = new Guid(0xac75d454, 0x9f37, 0x48f8, 0xb9, 0x72, 0x4e, 0x19, 0xbc, 0x85, 0x60, 0x11); + public static readonly Guid CLSID_WICFormatConverterNChannel = new Guid(0xc17cabb2, 0xd4a3, 0x47d7, 0xa5, 0x57, 0x33, 0x9b, 0x2e, 0xfb, 0xd4, 0xf1); + public static readonly Guid CLSID_WICFormatConverterWMPhoto = new Guid(0x9cb5172b, 0xd600, 0x46ba, 0xab, 0x77, 0x77, 0xbb, 0x7e, 0x3a, 0x00, 0xd9); + public static readonly Guid CLSID_WICPlanarFormatConverter = new Guid(0x184132b8, 0x32f8, 0x4784, 0x91, 0x31, 0xdd, 0x72, 0x24, 0xb2, 0x34, 0x38); + public static readonly Guid CLSID_WICGCEMetadataReader = new Guid(0xB92E345D, 0xF52D, 0x41F3, 0xB5, 0x62, 0x08, 0x1B, 0xC7, 0x72, 0xE3, 0xB9); + public static readonly Guid CLSID_WICGCEMetadataWriter = new Guid(0xAF95DC76, 0x16B2, 0x47F4, 0xB3, 0xEA, 0x3C, 0x31, 0x79, 0x66, 0x93, 0xE7); + public static readonly Guid CLSID_WICGifCommentMetadataReader = new Guid(0x32557D3B, 0x69DC, 0x4F95, 0x83, 0x6E, 0xF5, 0x97, 0x2B, 0x2F, 0x61, 0x59); + public static readonly Guid CLSID_WICGifCommentMetadataWriter = new Guid(0xA02797fC, 0xC4AE, 0x418C, 0xAF, 0x95, 0xE6, 0x37, 0xC7, 0xEA, 0xD2, 0xA1); + public static readonly Guid CLSID_WICGifDecoder = new Guid(0x381dda3c, 0x9ce9, 0x4834, 0xa2, 0x3e, 0x1f, 0x98, 0xf8, 0xfc, 0x52, 0xbe); + public static readonly Guid CLSID_WICGifEncoder = new Guid(0x114f5598, 0x0b22, 0x40a0, 0x86, 0xa1, 0xc8, 0x3e, 0xa4, 0x95, 0xad, 0xbd); + public static readonly Guid CLSID_WICGpsMetadataReader = new Guid(0x3697790B, 0x223B, 0x484E, 0x99, 0x25, 0xC4, 0x86, 0x92, 0x18, 0xF1, 0x7A); + public static readonly Guid CLSID_WICGpsMetadataWriter = new Guid(0xCB8C13E4, 0x62B5, 0x4C96, 0xA4, 0x8B, 0x6B, 0xA6, 0xAC, 0xE3, 0x9C, 0x76); + public static readonly Guid CLSID_WICIcoDecoder = new Guid(0xc61bfcdf, 0x2e0f, 0x4aad, 0xa8, 0xd7, 0xe0, 0x6b, 0xaf, 0xeb, 0xcd, 0xfe); + public static readonly Guid CLSID_WICIfdMetadataReader = new Guid(0x8f914656, 0x9d0a, 0x4eb2, 0x90, 0x19, 0x0b, 0xf9, 0x6d, 0x8a, 0x9e, 0xe6); + public static readonly Guid CLSID_WICIfdMetadataWriter = new Guid(0xb1ebfc28, 0xc9bd, 0x47a2, 0x8d, 0x33, 0xb9, 0x48, 0x76, 0x97, 0x77, 0xa7); + public static readonly Guid CLSID_WICImagingCategories = new Guid(0xfae3d380, 0xfea4, 0x4623, 0x8c, 0x75, 0xc6, 0xb6, 0x11, 0x10, 0xb6, 0x81); + public static readonly Guid CLSID_WICImagingFactory = new Guid(0xcacaf262, 0x9370, 0x4615, 0xa1, 0x3b, 0x9f, 0x55, 0x39, 0xda, 0x4c, 0x0a); + public static readonly Guid CLSID_WICImagingFactory1 = new Guid(0xcacaf262, 0x9370, 0x4615, 0xa1, 0x3b, 0x9f, 0x55, 0x39, 0xda, 0x4c, 0x0a); + public static readonly Guid CLSID_WICImagingFactory2 = new Guid(0x317d06e8, 0x5f24, 0x433d, 0xbd, 0xf7, 0x79, 0xce, 0x68, 0xd8, 0xab, 0xc2); + public static readonly Guid CLSID_WICIMDMetadataReader = new Guid(0x7447A267, 0x0015, 0x42C8, 0xA8, 0xF1, 0xFB, 0x3B, 0x94, 0xC6, 0x83, 0x61); + public static readonly Guid CLSID_WICIMDMetadataWriter = new Guid(0x8C89071F, 0x452E, 0x4E95, 0x96, 0x82, 0x9D, 0x10, 0x24, 0x62, 0x71, 0x72); + public static readonly Guid CLSID_WICInteropMetadataReader = new Guid(0xB5C8B898, 0x0074, 0x459F, 0xB7, 0x00, 0x86, 0x0D, 0x46, 0x51, 0xEA, 0x14); + public static readonly Guid CLSID_WICInteropMetadataWriter = new Guid(0x122EC645, 0xCD7E, 0x44D8, 0xB1, 0x86, 0x2C, 0x8C, 0x20, 0xC3, 0xB5, 0x0F); + public static readonly Guid CLSID_WICIPTCMetadataReader = new Guid(0x03012959, 0xf4f6, 0x44d7, 0x9d, 0x09, 0xda, 0xa0, 0x87, 0xa9, 0xdb, 0x57); + public static readonly Guid CLSID_WICIPTCMetadataWriter = new Guid(0x1249b20c, 0x5dd0, 0x44fe, 0xb0, 0xb3, 0x8f, 0x92, 0xc8, 0xe6, 0xd0, 0x80); + public static readonly Guid CLSID_WICIRBMetadataReader = new Guid(0xD4DCD3D7, 0xB4C2, 0x47D9, 0xA6, 0xBF, 0xB8, 0x9B, 0xA3, 0x96, 0xA4, 0xA3); + public static readonly Guid CLSID_WICIRBMetadataWriter = new Guid(0x5C5C1935, 0x0235, 0x4434, 0x80, 0xBC, 0x25, 0x1B, 0xC1, 0xEC, 0x39, 0xC6); + public static readonly Guid CLSID_WICJpegChrominanceMetadataReader = new Guid(0x50B1904B, 0xF28F, 0x4574, 0x93, 0xF4, 0x0B, 0xAD, 0xE8, 0x2C, 0x69, 0xE9); + public static readonly Guid CLSID_WICJpegChrominanceMetadataWriter = new Guid(0x3FF566F0, 0x6E6B, 0x49D4, 0x96, 0xE6, 0xB7, 0x88, 0x86, 0x69, 0x2C, 0x62); + public static readonly Guid CLSID_WICJpegCommentMetadataReader = new Guid(0x9f66347C, 0x60C4, 0x4C4D, 0xAB, 0x58, 0xD2, 0x35, 0x86, 0x85, 0xf6, 0x07); + public static readonly Guid CLSID_WICJpegCommentMetadataWriter = new Guid(0xE573236F, 0x55B1, 0x4EDA, 0x81, 0xEA, 0x9F, 0x65, 0xDB, 0x02, 0x90, 0xD3); + public static readonly Guid CLSID_WICJpegDecoder = new Guid(0x9456a480, 0xe88b, 0x43ea, 0x9e, 0x73, 0x0b, 0x2d, 0x9b, 0x71, 0xb1, 0xca); + public static readonly Guid CLSID_WICJpegEncoder = new Guid(0x1a34f5c1, 0x4a5a, 0x46dc, 0xb6, 0x44, 0x1f, 0x45, 0x67, 0xe7, 0xa6, 0x76); + public static readonly Guid CLSID_WICJpegLuminanceMetadataReader = new Guid(0x356F2F88, 0x05A6, 0x4728, 0xB9, 0xA4, 0x1B, 0xFB, 0xCE, 0x04, 0xD8, 0x38); + public static readonly Guid CLSID_WICJpegLuminanceMetadataWriter = new Guid(0x1D583ABC, 0x8A0E, 0x4657, 0x99, 0x82, 0xA3, 0x80, 0xCA, 0x58, 0xFB, 0x4B); + public static readonly Guid CLSID_WICLSDMetadataReader = new Guid(0x41070793, 0x59E4, 0x479A, 0xA1, 0xF7, 0x95, 0x4A, 0xDC, 0x2E, 0xF5, 0xFC); + public static readonly Guid CLSID_WICLSDMetadataWriter = new Guid(0x73C037E7, 0xE5D9, 0x4954, 0x87, 0x6A, 0x6D, 0xA8, 0x1D, 0x6E, 0x57, 0x68); + public static readonly Guid CLSID_WICPngBkgdMetadataReader = new Guid(0x0CE7A4A6, 0x03E8, 0x4A60, 0x9D, 0x15, 0x28, 0x2E, 0xF3, 0x2E, 0xE7, 0xDA); + public static readonly Guid CLSID_WICPngBkgdMetadataWriter = new Guid(0x68E3F2FD, 0x31AE, 0x4441, 0xBB, 0x6A, 0xFD, 0x70, 0x47, 0x52, 0x5F, 0x90); + public static readonly Guid CLSID_WICPngChrmMetadataReader = new Guid(0xF90B5F36, 0x367B, 0x402A, 0x9D, 0xD1, 0xBC, 0x0F, 0xD5, 0x9D, 0x8F, 0x62); + public static readonly Guid CLSID_WICPngChrmMetadataWriter = new Guid(0xE23CE3EB, 0x5608, 0x4E83, 0xBC, 0xEF, 0x27, 0xB1, 0x98, 0x7E, 0x51, 0xD7); + public static readonly Guid CLSID_WICPngDecoder = new Guid(0x389ea17b, 0x5078, 0x4cde, 0xb6, 0xef, 0x25, 0xc1, 0x51, 0x75, 0xc7, 0x51); + public static readonly Guid CLSID_WICPngDecoder1 = new Guid(0x389ea17b, 0x5078, 0x4cde, 0xb6, 0xef, 0x25, 0xc1, 0x51, 0x75, 0xc7, 0x51); + public static readonly Guid CLSID_WICPngDecoder2 = new Guid(0xe018945b, 0xaa86, 0x4008, 0x9b, 0xd4, 0x67, 0x77, 0xa1, 0xe4, 0x0c, 0x11); + public static readonly Guid CLSID_WICPngEncoder = new Guid(0x27949969, 0x876a, 0x41d7, 0x94, 0x47, 0x56, 0x8f, 0x6a, 0x35, 0xa4, 0xdc); + public static readonly Guid CLSID_WICPngGamaMetadataReader = new Guid(0x3692CA39, 0xE082, 0x4350, 0x9E, 0x1F, 0x37, 0x04, 0xCB, 0x08, 0x3C, 0xD5); + public static readonly Guid CLSID_WICPngGamaMetadataWriter = new Guid(0xFF036D13, 0x5D4B, 0x46DD, 0xB1, 0x0F, 0x10, 0x66, 0x93, 0xD9, 0xFE, 0x4F); + public static readonly Guid CLSID_WICPngHistMetadataReader = new Guid(0x877A0BB7, 0xA313, 0x4491, 0x87, 0xB5, 0x2E, 0x6D, 0x05, 0x94, 0xF5, 0x20); + public static readonly Guid CLSID_WICPngHistMetadataWriter = new Guid(0x8A03E749, 0x672E, 0x446E, 0xBF, 0x1F, 0x2C, 0x11, 0xD2, 0x33, 0xB6, 0xFF); + public static readonly Guid CLSID_WICPngIccpMetadataReader = new Guid(0xF5D3E63B, 0xCB0F, 0x4628, 0xA4, 0x78, 0x6D, 0x82, 0x44, 0xBE, 0x36, 0xB1); + public static readonly Guid CLSID_WICPngIccpMetadataWriter = new Guid(0x16671E5F, 0x0CE6, 0x4CC4, 0x97, 0x68, 0xE8, 0x9F, 0xE5, 0x01, 0x8A, 0xDE); + public static readonly Guid CLSID_WICPngItxtMetadataReader = new Guid(0xAABFB2FA, 0x3E1E, 0x4A8F, 0x89, 0x77, 0x55, 0x56, 0xFB, 0x94, 0xEA, 0x23); + public static readonly Guid CLSID_WICPngItxtMetadataWriter = new Guid(0x31879719, 0xE751, 0x4DF8, 0x98, 0x1D, 0x68, 0xDF, 0xF6, 0x77, 0x04, 0xED); + public static readonly Guid CLSID_WICPngSrgbMetadataReader = new Guid(0xFB40360C, 0x547E, 0x4956, 0xA3, 0xB9, 0xD4, 0x41, 0x88, 0x59, 0xBA, 0x66); + public static readonly Guid CLSID_WICPngSrgbMetadataWriter = new Guid(0xA6EE35C6, 0x87EC, 0x47DF, 0x9F, 0x22, 0x1D, 0x5A, 0xAD, 0x84, 0x0C, 0x82); + public static readonly Guid CLSID_WICPngTextMetadataReader = new Guid(0x4b59afcc, 0xb8c3, 0x408a, 0xb6, 0x70, 0x89, 0xe5, 0xfa, 0xb6, 0xfd, 0xa7); + public static readonly Guid CLSID_WICPngTextMetadataWriter = new Guid(0xb5ebafb9, 0x253e, 0x4a72, 0xa7, 0x44, 0x07, 0x62, 0xd2, 0x68, 0x56, 0x83); + public static readonly Guid CLSID_WICPngTimeMetadataReader = new Guid(0xD94EDF02, 0xEFE5, 0x4F0D, 0x85, 0xC8, 0xF5, 0xA6, 0x8B, 0x30, 0x00, 0xB1); + public static readonly Guid CLSID_WICPngTimeMetadataWriter = new Guid(0x1AB78400, 0xB5A3, 0x4D91, 0x8A, 0xCE, 0x33, 0xFC, 0xD1, 0x49, 0x9B, 0xE6); + public static readonly Guid CLSID_WICSubIfdMetadataReader = new Guid(0x50D42F09, 0xECD1, 0x4B41, 0xB6, 0x5D, 0xDA, 0x1F, 0xDA, 0xA7, 0x56, 0x63); + public static readonly Guid CLSID_WICSubIfdMetadataWriter = new Guid(0x8ADE5386, 0x8E9B, 0x4F4C, 0xAC, 0xF2, 0xF0, 0x00, 0x87, 0x06, 0xB2, 0x38); + public static readonly Guid CLSID_WICThumbnailMetadataReader = new Guid(0xfb012959, 0xf4f6, 0x44d7, 0x9d, 0x09, 0xda, 0xa0, 0x87, 0xa9, 0xdb, 0x57); + public static readonly Guid CLSID_WICThumbnailMetadataWriter = new Guid(0xd049b20c, 0x5dd0, 0x44fe, 0xb0, 0xb3, 0x8f, 0x92, 0xc8, 0xe6, 0xd0, 0x80); + public static readonly Guid CLSID_WICTiffDecoder = new Guid(0xb54e85d9, 0xfe23, 0x499f, 0x8b, 0x88, 0x6a, 0xce, 0xa7, 0x13, 0x75, 0x2b); + public static readonly Guid CLSID_WICTiffEncoder = new Guid(0x0131be10, 0x2001, 0x4c5f, 0xa9, 0xb0, 0xcc, 0x88, 0xfa, 0xb6, 0x4c, 0xe8); + public static readonly Guid CLSID_WICUnknownMetadataReader = new Guid(0x699745c2, 0x5066, 0x4b82, 0xa8, 0xe3, 0xd4, 0x04, 0x78, 0xdb, 0xec, 0x8c); + public static readonly Guid CLSID_WICUnknownMetadataWriter = new Guid(0xa09cca86, 0x27ba, 0x4f39, 0x90, 0x53, 0x12, 0x1f, 0xa4, 0xdc, 0x08, 0xfc); + public static readonly Guid CLSID_WICWmpDecoder = new Guid(0xa26cec36, 0x234c, 0x4950, 0xae, 0x16, 0xe3, 0x4a, 0xac, 0xe7, 0x1d, 0x0d); + public static readonly Guid CLSID_WICWmpEncoder = new Guid(0xac4ce3cb, 0xe1c1, 0x44cd, 0x82, 0x15, 0x5a, 0x16, 0x65, 0x50, 0x9e, 0xc2); + public static readonly Guid CLSID_WICXMPAltMetadataReader = new Guid(0xAA94DCC2, 0xB8B0, 0x4898, 0xB8, 0x35, 0x00, 0x0A, 0xAB, 0xD7, 0x43, 0x93); + public static readonly Guid CLSID_WICXMPAltMetadataWriter = new Guid(0x076C2A6C, 0xF78F, 0x4C46, 0xA7, 0x23, 0x35, 0x83, 0xE7, 0x08, 0x76, 0xEA); + public static readonly Guid CLSID_WICXMPBagMetadataReader = new Guid(0xE7E79A30, 0x4F2C, 0x4FAB, 0x8D, 0x00, 0x39, 0x4F, 0x2D, 0x6B, 0xBE, 0xBE); + public static readonly Guid CLSID_WICXMPBagMetadataWriter = new Guid(0xED822C8C, 0xD6BE, 0x4301, 0xA6, 0x31, 0x0E, 0x14, 0x16, 0xBA, 0xD2, 0x8F); + public static readonly Guid CLSID_WICXMPMetadataReader = new Guid(0x72B624DF, 0xAE11, 0x4948, 0xA6, 0x5C, 0x35, 0x1E, 0xB0, 0x82, 0x94, 0x19); + public static readonly Guid CLSID_WICXMPMetadataWriter = new Guid(0x1765E14E, 0x1BD4, 0x462E, 0xB6, 0xB1, 0x59, 0x0B, 0xF1, 0x26, 0x2A, 0xC6); + public static readonly Guid CLSID_WICXMPSeqMetadataReader = new Guid(0x7F12E753, 0xFC71, 0x43D7, 0xA5, 0x1D, 0x92, 0xF3, 0x59, 0x77, 0xAB, 0xB5); + public static readonly Guid CLSID_WICXMPSeqMetadataWriter = new Guid(0x6D68D1DE, 0xD432, 0x4B0F, 0x92, 0x3A, 0x09, 0x11, 0x83, 0xA9, 0xBD, 0xA7); + public static readonly Guid CLSID_WICXMPStructMetadataReader = new Guid(0x01B90D9A, 0x8209, 0x47F7, 0x9C, 0x52, 0xE1, 0x24, 0x4B, 0xF5, 0x0C, 0xED); + public static readonly Guid CLSID_WICXMPStructMetadataWriter = new Guid(0x22C21F93, 0x7DDB, 0x411C, 0x9B, 0x17, 0xC5, 0xB7, 0xBD, 0x06, 0x4A, 0xBC); + public static readonly Guid CLSID_WICDdsDecoder = new Guid(0x9053699f, 0xa341, 0x429d, 0x9e, 0x90, 0xee, 0x43, 0x7c, 0xf8, 0x0c, 0x73); + public static readonly Guid CLSID_WICDdsEncoder = new Guid(0xa61dde94, 0x66ce, 0x4ac1, 0x88, 0x1b, 0x71, 0x68, 0x05, 0x88, 0x89, 0x5e); + public static readonly Guid CLSID_WICJpegQualcommPhoneEncoder = new Guid(0x68ed5c62, 0xf534, 0x4979, 0xb2, 0xb3, 0x68, 0x6a, 0x12, 0xb2, 0xb3, 0x4c); + + public static readonly Guid GUID_ContainerFormatBmp = new Guid(0x0af1d87e, 0xfcfe, 0x4188, 0xbd, 0xeb, 0xa7, 0x90, 0x64, 0x71, 0xcb, 0xe3); + public static readonly Guid GUID_ContainerFormatGif = new Guid(0x1f8a5601, 0x7d4d, 0x4cbd, 0x9c, 0x82, 0x1b, 0xc8, 0xd4, 0xee, 0xb9, 0xa5); + public static readonly Guid GUID_ContainerFormatIco = new Guid(0xa3a860c4, 0x338f, 0x4c17, 0x91, 0x9a, 0xfb, 0xa4, 0xb5, 0x62, 0x8f, 0x21); + public static readonly Guid GUID_ContainerFormatJpeg = new Guid(0x19e4a5aa, 0x5662, 0x4fc5, 0xa0, 0xc0, 0x17, 0x58, 0x02, 0x8e, 0x10, 0x57); + public static readonly Guid GUID_ContainerFormatPng = new Guid(0x1b7cfaf4, 0x713f, 0x473c, 0xbb, 0xcd, 0x61, 0x37, 0x42, 0x5f, 0xae, 0xaf); + public static readonly Guid GUID_ContainerFormatTiff = new Guid(0x163bcc30, 0xe2e9, 0x4f0b, 0x96, 0x1d, 0xa3, 0xe9, 0xfd, 0xb7, 0x88, 0xa3); + public static readonly Guid GUID_ContainerFormatWmp = new Guid(0x57a37caa, 0x367a, 0x4540, 0x91, 0x6b, 0xf1, 0x83, 0xc5, 0x09, 0x3a, 0x4b); + public static readonly Guid GUID_ContainerFormatRaw = new Guid(0xc1fc85cb, 0xd64f, 0x478b, 0xa4, 0xec, 0x69, 0xad, 0xc9, 0xee, 0x13, 0x92); + + public static readonly Guid GUID_MetadataFormat8BIMIPTC = new Guid(0x0010568c, 0x0852, 0x4e6a, 0xb1, 0x91, 0x5c, 0x33, 0xac, 0x5b, 0x04, 0x30); + public static readonly Guid GUID_MetadataFormat8BIMResolutionInfo = new Guid(0x739F305D, 0x81DB, 0x43CB, 0xAC, 0x5E, 0x55, 0x01, 0x3E, 0xF9, 0xF0, 0x03); + public static readonly Guid GUID_MetadataFormatAPE = new Guid(0x2e043dc2, 0xC967, 0x4E05, 0x87, 0x5E, 0x61, 0x8B, 0xF6, 0x7E, 0x85, 0xC3); + public static readonly Guid GUID_MetadataFormatApp0 = new Guid(0x79007028, 0x268D, 0x45d6, 0xA3, 0xC2, 0x35, 0x4E, 0x6A, 0x50, 0x4B, 0xC9); + public static readonly Guid GUID_MetadataFormatApp1 = new Guid(0x8FD3DFC3, 0xF951, 0x492B, 0x81, 0x7F, 0x69, 0xC2, 0xE6, 0xD9, 0xA5, 0xB0); + public static readonly Guid GUID_MetadataFormatApp13 = new Guid(0x326556A2, 0xF502, 0x4354, 0x9C, 0xC0, 0x8E, 0x3F, 0x48, 0xEA, 0xF6, 0xB5); + public static readonly Guid GUID_MetadataFormatChunkbKGD = new Guid(0xE14D3571, 0x6B47, 0x4DEA, 0xB6, 0x0A, 0x87, 0xCE, 0x0A, 0x78, 0xDF, 0xB7); + public static readonly Guid GUID_MetadataFormatChunkcHRM = new Guid(0x9DB3655B, 0x2842, 0x44B3, 0x80, 0x67, 0x12, 0xE9, 0xB3, 0x75, 0x55, 0x6A); + public static readonly Guid GUID_MetadataFormatChunkgAMA = new Guid(0xF00935A5, 0x1D5D, 0x4CD1, 0x81, 0xB2, 0x93, 0x24, 0xD7, 0xEC, 0xA7, 0x81); + public static readonly Guid GUID_MetadataFormatChunkhIST = new Guid(0xC59A82DA, 0xDB74, 0x48A4, 0xBD, 0x6A, 0xB6, 0x9C, 0x49, 0x31, 0xEF, 0x95); + public static readonly Guid GUID_MetadataFormatChunkiCCP = new Guid(0xEB4349AB, 0xB685, 0x450F, 0x91, 0xB5, 0xE8, 0x02, 0xE8, 0x92, 0x53, 0x6C); + public static readonly Guid GUID_MetadataFormatChunkiTXt = new Guid(0xC2BEC729, 0x0B68, 0x4B77, 0xAA, 0x0E, 0x62, 0x95, 0xA6, 0xAC, 0x18, 0x14); + public static readonly Guid GUID_MetadataFormatChunksRGB = new Guid(0xC115FD36, 0xCC6F, 0x4E3F, 0x83, 0x63, 0x52, 0x4B, 0x87, 0xC6, 0xB0, 0xD9); + public static readonly Guid GUID_MetadataFormatChunktEXt = new Guid(0x568d8936, 0xc0a9, 0x4923, 0x90, 0x5d, 0xdf, 0x2b, 0x38, 0x23, 0x8f, 0xbc); + public static readonly Guid GUID_MetadataFormatChunktIME = new Guid(0x6B00AE2D, 0xE24B, 0x460A, 0x98, 0xB6, 0x87, 0x8B, 0xD0, 0x30, 0x72, 0xFD); + public static readonly Guid GUID_MetadataFormatExif = new Guid(0x1C3C4F9D, 0xB84A, 0x467D, 0x94, 0x93, 0x36, 0xCF, 0xBD, 0x59, 0xEA, 0x57); + public static readonly Guid GUID_MetadataFormatGCE = new Guid(0x2A25CAD8, 0xDEEB, 0x4C69, 0xA7, 0x88, 0x0E, 0xC2, 0x26, 0x6D, 0xCA, 0xFD); + public static readonly Guid GUID_MetadataFormatGifComment = new Guid(0xc4b6e0e0, 0xcfb4, 0x4ad3, 0xab, 0x33, 0x9a, 0xad, 0x23, 0x55, 0xa3, 0x4a); + public static readonly Guid GUID_MetadataFormatGps = new Guid(0x7134AB8A, 0x9351, 0x44AD, 0xAF, 0x62, 0x44, 0x8D, 0xB6, 0xB5, 0x02, 0xEC); + public static readonly Guid GUID_MetadataFormatIfd = new Guid(0x537396C6, 0x2D8A, 0x4BB6, 0x9B, 0xF8, 0x2F, 0x0A, 0x8E, 0x2A, 0x3A, 0xDF); + public static readonly Guid GUID_MetadataFormatIMD = new Guid(0xBD2BB086, 0x4D52, 0x48DD, 0x96, 0x77, 0xDB, 0x48, 0x3E, 0x85, 0xAE, 0x8F); + public static readonly Guid GUID_MetadataFormatInterop = new Guid(0xED686F8E, 0x681F, 0x4C8B, 0xBD, 0x41, 0xA8, 0xAD, 0xDB, 0xF6, 0xB3, 0xFC); + public static readonly Guid GUID_MetadataFormatIPTC = new Guid(0x4FAB0914, 0xE129, 0x4087, 0xA1, 0xD1, 0xBC, 0x81, 0x2D, 0x45, 0xA7, 0xB5); + public static readonly Guid GUID_MetadataFormatIPTCDigest = new Guid(0x1CA32285, 0x9CCD, 0x4786, 0x8B, 0xD8, 0x79, 0x53, 0x9D, 0xB6, 0xA0, 0x06); + public static readonly Guid GUID_MetadataFormatIPTCDigestReader = new Guid(0x02805F1E, 0xD5AA, 0x415b, 0x82, 0xC5, 0x61, 0xC0, 0x33, 0xA9, 0x88, 0xA6); + public static readonly Guid GUID_MetadataFormatIPTCDigestWriter = new Guid(0x2DB5E62B, 0x0D67, 0x495f, 0x8F, 0x9D, 0xC2, 0xF0, 0x18, 0x86, 0x47, 0xAC); + public static readonly Guid GUID_MetadataFormatIRB = new Guid(0x16100D66, 0x8570, 0x4BB9, 0xB9, 0x2D, 0xFD, 0xA4, 0xB2, 0x3E, 0xCE, 0x67); + public static readonly Guid GUID_MetadataFormatJpegChrominance = new Guid(0xF73D0DCF, 0xCEC6, 0x4F85, 0x9B, 0x0E, 0x1C, 0x39, 0x56, 0xB1, 0xBE, 0xF7); + public static readonly Guid GUID_MetadataFormatJpegComment = new Guid(0x220E5F33, 0xAFD3, 0x474E, 0x9D, 0x31, 0x7D, 0x4F, 0xE7, 0x30, 0xF5, 0x57); + public static readonly Guid GUID_MetadataFormatJpegLuminance = new Guid(0x86908007, 0xEDFC, 0x4860, 0x8D, 0x4B, 0x4E, 0xE6, 0xE8, 0x3E, 0x60, 0x58); + public static readonly Guid GUID_MetadataFormatLSD = new Guid(0xE256031E, 0x6299, 0x4929, 0xB9, 0x8D, 0x5A, 0xC8, 0x84, 0xAF, 0xBA, 0x92); + public static readonly Guid GUID_MetadataFormatSubIfd = new Guid(0x58A2E128, 0x2DB9, 0x4E57, 0xBB, 0x14, 0x51, 0x77, 0x89, 0x1E, 0xD3, 0x31); + public static readonly Guid GUID_MetadataFormatThumbnail = new Guid(0x243dcee9, 0x8703, 0x40ee, 0x8e, 0xf0, 0x22, 0xa6, 0x00, 0xb8, 0x05, 0x8c); + public static readonly Guid GUID_MetadataFormatUnknown = new Guid(0xA45E592F, 0x9078, 0x4A7C, 0xAD, 0xB5, 0x4E, 0xDC, 0x4F, 0xD6, 0x1B, 0x1F); + public static readonly Guid GUID_MetadataFormatXMP = new Guid(0xBB5ACC38, 0xF216, 0x4CEC, 0xA6, 0xC5, 0x5F, 0x6E, 0x73, 0x97, 0x63, 0xA9); + public static readonly Guid GUID_MetadataFormatXMPAlt = new Guid(0x7B08A675, 0x91AA, 0x481B, 0xA7, 0x98, 0x4D, 0xA9, 0x49, 0x08, 0x61, 0x3B); + public static readonly Guid GUID_MetadataFormatXMPBag = new Guid(0x833CCA5F, 0xDCB7, 0x4516, 0x80, 0x6F, 0x65, 0x96, 0xAB, 0x26, 0xDC, 0xE4); + public static readonly Guid GUID_MetadataFormatXMPSeq = new Guid(0x63E8DF02, 0xEB6C, 0x456C, 0xA2, 0x24, 0xB2, 0x5E, 0x79, 0x4F, 0xD6, 0x48); + public static readonly Guid GUID_MetadataFormatXMPStruct = new Guid(0x22383CF1, 0xED17, 0x4E2E, 0xAF, 0x17, 0xD8, 0x5B, 0x8F, 0x6B, 0x30, 0xD0); + + public static readonly Guid GUID_VendorMicrosoft = new Guid(0xf0e749ca, 0xedef, 0x4589, 0xa7, 0x3a, 0xee, 0xe, 0x62, 0x6a, 0x2a, 0x2b); + public static readonly Guid GUID_VendorMicrosoftBuiltIn = new Guid(0x257a30fd, 0x06b6, 0x462b, 0xae, 0xa4, 0x63, 0xf7, 0xb, 0x86, 0xe5, 0x33); + + public static readonly Guid GUID_WICPixelFormat112bpp6ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x37); + public static readonly Guid GUID_WICPixelFormat112bpp7Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2a); + public static readonly Guid GUID_WICPixelFormat128bpp7ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x38); + public static readonly Guid GUID_WICPixelFormat128bpp8Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2b); + public static readonly Guid GUID_WICPixelFormat128bppPRGBAFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1a); + public static readonly Guid GUID_WICPixelFormat128bppRGBAFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1e); + public static readonly Guid GUID_WICPixelFormat128bppRGBAFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x19); + public static readonly Guid GUID_WICPixelFormat128bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x41); + public static readonly Guid GUID_WICPixelFormat128bppRGBFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1b); + public static readonly Guid GUID_WICPixelFormat144bpp8ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x39); + public static readonly Guid GUID_WICPixelFormat16bppBGR555 = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x09); + public static readonly Guid GUID_WICPixelFormat16bppBGR565 = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0a); + public static readonly Guid GUID_WICPixelFormat16bppBGRA5551 = new Guid(0x05ec7c2b, 0xf1e6, 0x4961, 0xad, 0x46, 0xe1, 0xcc, 0x81, 0x0a, 0x87, 0xd2); + public static readonly Guid GUID_WICPixelFormat16bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0b); + public static readonly Guid GUID_WICPixelFormat16bppGrayFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x13); + public static readonly Guid GUID_WICPixelFormat16bppGrayHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3e); + public static readonly Guid GUID_WICPixelFormat1bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x01); + public static readonly Guid GUID_WICPixelFormat24bpp3Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x20); + public static readonly Guid GUID_WICPixelFormat24bppBGR = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c); + public static readonly Guid GUID_WICPixelFormat24bppRGB = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d); + public static readonly Guid GUID_WICPixelFormat2bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x06); + public static readonly Guid GUID_WICPixelFormat2bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x02); + public static readonly Guid GUID_WICPixelFormat32bpp3ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2e); + public static readonly Guid GUID_WICPixelFormat32bpp4Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x21); + public static readonly Guid GUID_WICPixelFormat32bppBGR = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0e); + public static readonly Guid GUID_WICPixelFormat32bppBGR101010 = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x14); + public static readonly Guid GUID_WICPixelFormat32bppBGRA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f); + public static readonly Guid GUID_WICPixelFormat32bppCMYK = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1c); + public static readonly Guid GUID_WICPixelFormat32bppGrayFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3f); + public static readonly Guid GUID_WICPixelFormat32bppGrayFloat = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x11); + public static readonly Guid GUID_WICPixelFormat32bppPBGRA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x10); + public static readonly Guid GUID_WICPixelFormat32bppPRGBA = new Guid(0x3cc4a650, 0xa527, 0x4d37, 0xa9, 0x16, 0x31, 0x42, 0xc7, 0xeb, 0xed, 0xba); + public static readonly Guid GUID_WICPixelFormat32bppRGB = new Guid(0xd98c6b95, 0x3efe, 0x47d6, 0xbb, 0x25, 0xeb, 0x17, 0x48, 0xab, 0x0c, 0xf1); + public static readonly Guid GUID_WICPixelFormat32bppRGBA = new Guid(0xf5c7ad2d, 0x6a8d, 0x43dd, 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9); + public static readonly Guid GUID_WICPixelFormat32bppRGBA1010102 = new Guid(0x25238D72, 0xFCF9, 0x4522, 0xb5, 0x14, 0x55, 0x78, 0xe5, 0xad, 0x55, 0xe0); + public static readonly Guid GUID_WICPixelFormat32bppRGBA1010102XR = new Guid(0x00DE6B9A, 0xC101, 0x434b, 0xb5, 0x02, 0xd0, 0x16, 0x5e, 0xe1, 0x12, 0x2c); + public static readonly Guid GUID_WICPixelFormat32bppRGBE = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3d); + public static readonly Guid GUID_WICPixelFormat40bpp4ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2f); + public static readonly Guid GUID_WICPixelFormat40bpp5Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x22); + public static readonly Guid GUID_WICPixelFormat40bppCMYKAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2c); + public static readonly Guid GUID_WICPixelFormat48bpp3Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x26); + public static readonly Guid GUID_WICPixelFormat48bpp5ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x30); + public static readonly Guid GUID_WICPixelFormat48bpp6Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x23); + public static readonly Guid GUID_WICPixelFormat48bppBGR = new Guid(0xe605a384, 0xb468, 0x46ce, 0xbb, 0x2e, 0x36, 0xf1, 0x80, 0xe6, 0x43, 0x13); + public static readonly Guid GUID_WICPixelFormat48bppBGRFixedPoint = new Guid(0x49ca140e, 0xcab6, 0x493b, 0x9d, 0xdf, 0x60, 0x18, 0x7c, 0x37, 0x53, 0x2a); + public static readonly Guid GUID_WICPixelFormat48bppRGB = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x15); + public static readonly Guid GUID_WICPixelFormat48bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x12); + public static readonly Guid GUID_WICPixelFormat48bppRGBHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3b); + public static readonly Guid GUID_WICPixelFormat4bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x07); + public static readonly Guid GUID_WICPixelFormat4bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x03); + public static readonly Guid GUID_WICPixelFormat56bpp6ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x31); + public static readonly Guid GUID_WICPixelFormat56bpp7Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x24); + public static readonly Guid GUID_WICPixelFormat64bpp3ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x34); + public static readonly Guid GUID_WICPixelFormat64bpp4Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x27); + public static readonly Guid GUID_WICPixelFormat64bpp7ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x32); + public static readonly Guid GUID_WICPixelFormat64bpp8Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x25); + public static readonly Guid GUID_WICPixelFormat64bppBGRA = new Guid(0x1562ff7c, 0xd352, 0x46f9, 0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46); + public static readonly Guid GUID_WICPixelFormat64bppBGRAFixedPoint = new Guid(0x356de33c, 0x54d2, 0x4a23, 0xbb, 0x04, 0x9b, 0x7b, 0xf9, 0xb1, 0xd4, 0x2d); + public static readonly Guid GUID_WICPixelFormat64bppCMYK = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1f); + public static readonly Guid GUID_WICPixelFormat64bppPBGRA = new Guid(0x8c518e8e, 0xa4ec, 0x468b, 0xae, 0x70, 0xc9, 0xa3, 0x5a, 0x9c, 0x55, 0x30); + public static readonly Guid GUID_WICPixelFormat64bppPRGBA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x17); + public static readonly Guid GUID_WICPixelFormat64bppRGB = new Guid(0xa1182111, 0x186d, 0x4d42, 0xbc, 0x6a, 0x9c, 0x83, 0x03, 0xa8, 0xdf, 0xf9); + public static readonly Guid GUID_WICPixelFormat64bppRGBA = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16); + public static readonly Guid GUID_WICPixelFormat64bppRGBAFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1d); + public static readonly Guid GUID_WICPixelFormat64bppRGBAHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x3a); + public static readonly Guid GUID_WICPixelFormat64bppPRGBAHalf = new Guid(0x58ad26c2, 0xc623, 0x4d9d, 0xb3, 0x20, 0x38, 0x7e, 0x49, 0xf8, 0xc4, 0x42); + public static readonly Guid GUID_WICPixelFormat64bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x40); + public static readonly Guid GUID_WICPixelFormat64bppRGBHalf = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x42); + public static readonly Guid GUID_WICPixelFormat72bpp8ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x33); + public static readonly Guid GUID_WICPixelFormat80bpp4ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x35); + public static readonly Guid GUID_WICPixelFormat80bpp5Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x28); + public static readonly Guid GUID_WICPixelFormat80bppCMYKAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x2d); + public static readonly Guid GUID_WICPixelFormat8bppAlpha = new Guid(0xe6cd0116, 0xeeba, 0x4161, 0xaa, 0x85, 0x27, 0xdd, 0x9f, 0xb3, 0xa8, 0x95); + public static readonly Guid GUID_WICPixelFormat8bppGray = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x08); + public static readonly Guid GUID_WICPixelFormat8bppIndexed = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x04); + public static readonly Guid GUID_WICPixelFormat8bppCb = new Guid(0x1339F224, 0x6BFE, 0x4C3E, 0x93, 0x02, 0xE4, 0xF3, 0xA6, 0xD0, 0xCA, 0x2A); + public static readonly Guid GUID_WICPixelFormat8bppCr = new Guid(0xB8145053, 0x2116, 0x49F0, 0x88, 0x35, 0xED, 0x84, 0x4B, 0x20, 0x5C, 0x51); + public static readonly Guid GUID_WICPixelFormat8bppY = new Guid(0x91B4DB54, 0x2DF9, 0x42F0, 0xB4, 0x49, 0x29, 0x09, 0xBB, 0x3D, 0xF8, 0x8E); + public static readonly Guid GUID_WICPixelFormat16bppCbCr = new Guid(0xFF95BA6E, 0x11E0, 0x4263, 0xBB, 0x45, 0x01, 0x72, 0x1F, 0x34, 0x60, 0xA4); + public static readonly Guid GUID_WICPixelFormat16bppCbQuantizedDctCoefficients = new Guid(0xD2C4FF61, 0x56A5, 0x49C2, 0x8B, 0x5C, 0x4C, 0x19, 0x25, 0x96, 0x48, 0x37); + public static readonly Guid GUID_WICPixelFormat16bppCrQuantizedDctCoefficients = new Guid(0x2FE354F0, 0x1680, 0x42D8, 0x92, 0x31, 0xE7, 0x3C, 0x05, 0x65, 0xBF, 0xC1); + public static readonly Guid GUID_WICPixelFormat16bppYQuantizedDctCoefficients = new Guid(0xA355F433, 0x48E8, 0x4A42, 0x84, 0xD8, 0xE2, 0xAA, 0x26, 0xCA, 0x80, 0xA4); + public static readonly Guid GUID_WICPixelFormat96bpp5ChannelsAlpha = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x36); + public static readonly Guid GUID_WICPixelFormat96bpp6Channels = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x29); + public static readonly Guid GUID_WICPixelFormat96bppRGBFixedPoint = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x18); + public static readonly Guid GUID_WICPixelFormat96bppRGBFloat = new Guid(0xe3fed78f, 0xe8db, 0x4acf, 0x84, 0xc1, 0xe9, 0x7f, 0x61, 0x36, 0xb3, 0x27); + public static readonly Guid GUID_WICPixelFormatBlackWhite = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x05); + public static readonly Guid GUID_WICPixelFormatDontCare = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x00); + public static readonly Guid GUID_WICPixelFormatUndefined = GUID_WICPixelFormatDontCare; + } + + [StructLayout(LayoutKind.Sequential)] + internal class WICRect + { + public int X; + public int Y; + public int Width; + public int Height; + } + + internal enum WICColorContextType : uint + { + WICColorContextUninitialized = 0x00000000, + WICColorContextProfile = 0x00000001, + WICColorContextExifColorSpace = 0x00000002, + } + + internal enum WICBitmapCreateCacheOption : uint + { + WICBitmapNoCache = 0x00000000, + WICBitmapCacheOnDemand = 0x00000001, + WICBitmapCacheOnLoad = 0x00000002, + } + + internal enum WICDecodeOptions : uint + { + WICDecodeMetadataCacheOnDemand = 0x00000000, + WICDecodeMetadataCacheOnLoad = 0x00000001, + } + + internal enum WICBitmapEncoderCacheOption : uint + { + WICBitmapEncoderCacheInMemory = 0x00000000, + WICBitmapEncoderCacheTempFile = 0x00000001, + WICBitmapEncoderNoCache = 0x00000002, + } + + [Flags] + internal enum WICComponentType : uint + { + WICDecoder = 0x00000001, + WICEncoder = 0x00000002, + WICPixelFormatConverter = 0x00000004, + WICMetadataReader = 0x00000008, + WICMetadataWriter = 0x00000010, + WICPixelFormat = 0x00000020, + WICAllComponents = 0x0000003F, + } + + internal enum WICComponentEnumerateOptions : uint + { + WICComponentEnumerateDefault = 0x00000000, + WICComponentEnumerateRefresh = 0x00000001, + WICComponentEnumerateDisabled = 0x80000000, + WICComponentEnumerateUnsigned = 0x40000000, + } + + internal struct WICBitmapPattern + { + public ulong Position; + public uint Length; + public IntPtr Pattern; + public IntPtr Mask; + [MarshalAs(UnmanagedType.Bool)] + public bool EndOfStream; + } + + internal enum WICBitmapInterpolationMode : uint + { + WICBitmapInterpolationModeNearestNeighbor = 0x00000000, + WICBitmapInterpolationModeLinear = 0x00000001, + WICBitmapInterpolationModeCubic = 0x00000002, + WICBitmapInterpolationModeFant = 0x00000003, + WICBitmapInterpolationModeHighQualityCubic = 0x00000004, + } + + internal enum WICBitmapPaletteType : uint + { + 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 + } + + internal enum WICBitmapDitherType : uint + { + WICBitmapDitherTypeNone = 0x00000000, + WICBitmapDitherTypeSolid = 0x00000000, + WICBitmapDitherTypeOrdered4x4 = 0x00000001, + WICBitmapDitherTypeOrdered8x8 = 0x00000002, + WICBitmapDitherTypeOrdered16x16 = 0x00000003, + WICBitmapDitherTypeSpiral4x4 = 0x00000004, + WICBitmapDitherTypeSpiral8x8 = 0x00000005, + WICBitmapDitherTypeDualSpiral4x4 = 0x00000006, + WICBitmapDitherTypeDualSpiral8x8 = 0x00000007, + WICBitmapDitherTypeErrorDiffusion = 0x00000008, + } + + internal enum WICBitmapAlphaChannelOption : uint + { + WICBitmapUseAlpha = 0x00000000, + WICBitmapUsePremultipliedAlpha = 0x00000001, + WICBitmapIgnoreAlpha = 0x00000002, + } + + [Flags] + internal enum WICBitmapTransformOptions : uint + { + WICBitmapTransformRotate0 = 0x00000000, + WICBitmapTransformRotate90 = 0x00000001, + WICBitmapTransformRotate180 = 0x00000002, + WICBitmapTransformRotate270 = 0x00000003, + WICBitmapTransformFlipHorizontal = 0x00000008, + WICBitmapTransformFlipVertical = 0x00000010, + } + + [Flags] + internal enum WICBitmapLockFlags : uint + { + WICBitmapLockRead = 0x00000001, + WICBitmapLockWrite = 0x00000002, + } + + [Flags] + internal enum WICBitmapDecoderCapabilities : uint + { + WICBitmapDecoderCapabilitySameEncoder = 0x00000001, + WICBitmapDecoderCapabilityCanDecodeAllImages = 0x00000002, + WICBitmapDecoderCapabilityCanDecodeSomeImages = 0x00000004, + WICBitmapDecoderCapabilityCanEnumerateMetadata = 0x00000008, + WICBitmapDecoderCapabilityCanDecodeThumbnail = 0x00000010, + } + + internal enum WICProgressOperation : uint + { + WICProgressOperationCopyPixels = 0x00000001, + WICProgressOperationWritePixels = 0x00000002, + WICProgressOperationAll = 0x0000FFFF, + } + + internal enum WICProgressNotification : uint + { + WICProgressNotificationBegin = 0x00010000, + WICProgressNotificationEnd = 0x00020000, + WICProgressNotificationFrequent = 0x00040000, + WICProgressNotificationAll = 0xFFFF0000, + } + + [Flags] + internal enum WICComponentSigning : uint + { + WICComponentSigned = 0x00000001, + WICComponentUnsigned = 0x00000002, + WICComponentSafe = 0x00000004, + WICComponentDisabled = 0x80000000, + } + + [ComImport, Guid("00000040-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICPalette + { + void InitializePredefined( + WICBitmapPaletteType ePaletteType, + [MarshalAs(UnmanagedType.Bool)] + bool fAddTransparentColor + ); + + void InitializeCustom( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + uint[] pColors, + uint cCount + ); + + void InitializeFromBitmap( + IWICBitmapSource pISurface, + uint cCount, + [MarshalAs(UnmanagedType.Bool)] + bool fAddTransparentColor + ); + + void InitializeFromPalette( + IWICPalette pIPalette + ); + + WICBitmapPaletteType GetType(); + + uint GetColorCount(); + + uint GetColors( + uint cCount, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + uint[] pColors + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool IsBlackWhite(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool IsGrayscale(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool HasAlpha(); + } + + [ComImport, Guid("00000120-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapSource + { + void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + Guid GetPixelFormat(); + + void GetResolution( + out double pDpiX, + out double pDpiY + ); + + void CopyPalette( + IWICPalette pIPalette + ); + + void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + } + + [ComImport, Guid("00000301-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICFormatConverter : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + void Initialize( + IWICBitmapSource pISource, + [MarshalAs(UnmanagedType.LPStruct)] + Guid dstFormat, + WICBitmapDitherType dither, + IWICPalette pIPalette, + double alphaThresholdPercent, + WICBitmapPaletteType paletteTranslate + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool CanConvert( + [MarshalAs(UnmanagedType.LPStruct)] + Guid srcPixelFormat, + [MarshalAs(UnmanagedType.LPStruct)] + Guid dstPixelFormat + ); + } + + [ComImport, Guid("00000302-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapScaler : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + void Initialize( + IWICBitmapSource pISource, + uint uiWidth, + uint uiHeight, + WICBitmapInterpolationMode mode + ); + } + + [ComImport, Guid("E4FBCF03-223D-4e81-9333-D635556DD1B5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapClipper : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + void Initialize( + IWICBitmapSource pISource, + WICRect prc + ); + } + + [ComImport, Guid("5009834F-2D6A-41ce-9E1B-17C5AFF7A782"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapFlipRotator : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + void Initialize( + IWICBitmapSource pISource, + WICBitmapTransformOptions options + ); + } + + [ComImport, Guid("00000123-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapLock + { + void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + uint GetStride(); + + void GetDataPointer( + out uint pcbBufferSize, + out IntPtr ppbData + ); + + Guid GetPixelFormat(); + } + + [ComImport, Guid("00000121-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmap : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + IWICBitmapLock Lock( + WICRect prcLock, + WICBitmapLockFlags flags + ); + + void SetPalette( + IWICPalette pIPalette + ); + + void SetResolution( + double dpiX, + double dpiY + ); + } + + internal enum ExifColorSpace : uint + { + sRGB = 0x00000001, + AdobeRGB = 0x00000002, + Uncalibrated = 0x0000ffff, + } + + [ComImport, Guid("3C613A02-34B2-44ea-9A7C-45AEA9C6FD6D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICColorContext + { + void InitializeFromFilename( + [MarshalAs(UnmanagedType.LPWStr)] + string wzFilename + ); + + void InitializeFromMemory( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] pbBuffer, + uint cbBufferSize + ); + + void InitializeFromExifColorSpace( + ExifColorSpace value + ); + + WICColorContextType GetType(); + + uint GetProfileBytes( + uint cbBuffer, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + byte[] pbBuffer + ); + + ExifColorSpace GetExifColorSpace(); + } + + [ComImport, Guid("B66F034F-D0E2-40ab-B436-6DE39E321A94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICColorTransform : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + void Initialize( + IWICBitmapSource pIBitmapSource, + IWICColorContext pIContextSource, + IWICColorContext pIContextDest, + [MarshalAs(UnmanagedType.LPStruct)] + Guid pixelFmtDest + ); + } + + [ComImport, Guid("B84E2C09-78C9-4AC4-8BD3-524AE1663A2F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICFastMetadataEncoder + { + void Commit(); + + IWICMetadataQueryWriter GetMetadataQueryWriter(); + } + + [ComImport, Guid("135FF860-22B7-4ddf-B0F6-218F4F299A43"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICStream : IStream + { + #region IStream + new void Read( + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] pv, + int cb, + IntPtr pcbRead + ); + + new void Write( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] pv, + int cb, + IntPtr pcbWritten + ); + + new void Seek( + long dlibMove, + int dwOrigin, + IntPtr plibNewPosition + ); + + new void SetSize( + long libNewSize + ); + + new void CopyTo( + IStream pstm, + long cb, + IntPtr pcbRead, + IntPtr pcbWritten + ); + + new void Commit( + int grfCommitFlags + ); + + new void Revert(); + + new void LockRegion( + long libOffset, + long cb, + int dwLockType + ); + + new void UnlockRegion( + long libOffset, + long cb, + int dwLockType + ); + + new void Stat( + out STATSTG pstatstg, + int grfStatFlag + ); + + new void Clone( + out IStream ppstm + ); + #endregion IStream + + void InitializeFromIStream( + IStream pIStream + ); + + void InitializeFromFilename( + [MarshalAs(UnmanagedType.LPWStr)] + string wzFileName, + GenericAccessRights dwDesiredAccess + ); + + void InitializeFromMemory( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] pbBuffer, + uint cbBufferSize + ); + + void InitializeFromIStreamRegion( + IStream pIStream, + ulong ulOffset, + ulong ulMaxSize + ); + } + + [ComImport, Guid("DC2BB46D-3F07-481E-8625-220C4AEDBB33"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICEnumMetadataItem + { + uint Next( + uint celt, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant rgeltSchema, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant rgeltId, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant rgeltValue + ); + + void Skip( + uint celt + ); + + void Reset(); + + IWICEnumMetadataItem Clone(); + } + + [ComImport, Guid("30989668-E1C9-4597-B395-458EEDB808DF"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataQueryReader + { + Guid GetContainerFormat(); + + uint GetLocation( + uint cchMaxLength, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzNamespace + ); + + void GetMetadataByName( + [MarshalAs(UnmanagedType.LPWStr)] + string wzName, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + IEnumString GetEnumerator(); + } + + [ComImport, Guid("A721791A-0DEF-4d06-BD91-2118BF1DB10B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataQueryWriter : IWICMetadataQueryReader + { + #region IWICMetadataQueryReader + new Guid GetContainerFormat(); + + new uint GetLocation( + uint cchMaxLength, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzNamespace + ); + + new void GetMetadataByName( + [MarshalAs(UnmanagedType.LPWStr)] + string wzName, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + new IEnumString GetEnumerator(); + #endregion + + void SetMetadataByName( + [MarshalAs(UnmanagedType.LPWStr)] + string wzName, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + void RemoveMetadataByName( + [MarshalAs(UnmanagedType.LPWStr)] + string wzName + ); + } + + [ComImport, Guid("00000103-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapEncoder + { + void Initialize( + IStream pIStream, + WICBitmapEncoderCacheOption cacheOption + ); + + Guid GetContainerFormat(); + + IWICBitmapEncoderInfo GetEncoderInfo(); + + void SetColorContexts( + uint cCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + IWICColorContext[] ppIColorContext + ); + + void SetPalette( + IWICPalette pIPalette + ); + + void SetThumbnail( + IWICBitmapSource pIThumbnail + ); + + void SetPreview( + IWICBitmapSource pIPreview + ); + + void CreateNewFrame( + out IWICBitmapFrameEncode ppIFrameEncode, + ref IPropertyBag2 ppIEncoderOptions + ); + + void Commit(); + + IWICMetadataQueryWriter GetMetadataQueryWriter(); + } + + [ComImport, Guid("00000105-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapFrameEncode + { + void Initialize( + IPropertyBag2 pIEncoderOptions + ); + + void SetSize( + uint uiWidth, + uint uiHeight + ); + + void SetResolution( + double dpiX, + double dpiY + ); + + void SetPixelFormat( + ref Guid pPixelFormat + ); + + void SetColorContexts( + uint cCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + IWICColorContext[] ppIColorContext + ); + + void SetPalette( + IWICPalette pIPalette + ); + + void SetThumbnail( + IWICBitmapSource pIThumbnail + ); + + void WritePixels( + uint lineCount, + uint cbStride, + uint cbBufferSize, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + byte[] pbPixels + ); + + void WriteSource( + IWICBitmapSource pIBitmapSource, + WICRect prc + ); + + void Commit(); + + IWICMetadataQueryWriter GetMetadataQueryWriter(); + } + + [ComImport, Guid("9EDDE9E7-8DEE-47ea-99DF-E6FAF2ED44BF"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapDecoder + { + WICBitmapDecoderCapabilities QueryCapability( + IStream pIStream + ); + + void Initialize( + IStream pIStream, + WICDecodeOptions cacheOptions + ); + + Guid GetContainerFormat(); + + IWICBitmapDecoderInfo GetDecoderInfo(); + + void CopyPalette( + IWICPalette pIPalette + ); + + IWICMetadataQueryReader GetMetadataQueryReader(); + + IWICBitmapSource GetPreview(); + + uint GetColorContexts( + uint cCount, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + IWICColorContext[] ppIColorContexts + ); + + IWICBitmapSource GetThumbnail(); + + uint GetFrameCount(); + + IWICBitmapFrameDecode GetFrame( + uint index + ); + } + + [ComImport, Guid("3B16811B-6A43-4ec9-B713-3D5A0C13B940"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapSourceTransform + { + void CopyPixels( + WICRect prcSrc, + uint uiWidth, + uint uiHeight, + [MarshalAs(UnmanagedType.LPStruct)] + Guid pguidDstFormat, + WICBitmapTransformOptions dstTransform, + uint nStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + + void GetClosestSize( + ref uint puiWidth, + ref uint puiHeight + ); + + void GetClosestPixelFormat( + ref Guid pguidDstFormat + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportTransform( + WICBitmapTransformOptions dstTransform + ); + } + + [ComImport, Guid("3B16811B-6A43-4ec9-A813-3D930C13B940"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapFrameDecode : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + IWICMetadataQueryReader GetMetadataQueryReader(); + + uint GetColorContexts( + uint cCount, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + IWICColorContext[] pIColorContexts + ); + + IWICBitmapSource GetThumbnail(); + } + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.Error)] + internal delegate int PFNProgressNotification( + IntPtr pvData, + uint uFrameNum, + WICProgressOperation operation, + double dblProgress + ); + + [ComImport, Guid("64C1024E-C3CF-4462-8078-88C2B11C46D9"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapCodecProgressNotification + { + void RegisterProgressNotification( + [MarshalAs(UnmanagedType.FunctionPtr)] + PFNProgressNotification pfnProgressNotification, + IntPtr pvData, + uint dwProgressFlags /* WICProgressOperation | WICProgressNotification */ + ); + } + + [ComImport, Guid("23BC3F0A-698B-4357-886B-F24D50671334"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICComponentInfo +{ + WICComponentType GetComponentType(); + + Guid GetCLSID(); + + WICComponentSigning GetSigningStatus(); + + uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + Guid GetVendorGUID(); + + uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + } + + [ComImport, Guid("9F34FB65-13F4-4f15-BC57-3726B5E53D9F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICFormatConverterInfo : IWICComponentInfo + { + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion IWICComponentInfo + + uint GetPixelFormats( + uint cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + Guid[] pPixelFormatGUIDs + ); + + IWICFormatConverter CreateInstance(); + } + + [ComImport, Guid("E87A44C4-B76E-4c47-8B09-298EB12A2714"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapCodecInfo : IWICComponentInfo + { + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion IWICComponentInfo + + Guid GetContainerFormat(); + + uint GetPixelFormats( + uint cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + Guid[] pguidPixelFormats + ); + + uint GetColorManagementVersion( + uint cchColorManagementVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzColorManagementVersion + ); + + uint GetDeviceManufacturer( + uint cchDeviceManufacturer, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceManufacturer + ); + + uint GetDeviceModels( + uint cchDeviceModels, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceModels + ); + + uint GetMimeTypes( + uint cchMimeTypes, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzMimeTypes + ); + + uint GetFileExtensions( + uint cchFileExtensions, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFileExtensions + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportAnimation(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportChromakey(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportLossless(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportMultiframe(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool MatchesMimeType( + [MarshalAs(UnmanagedType.LPWStr)] + string wzMimeType + ); + } + + [ComImport, Guid("94C9B4EE-A09F-4f92-8A1E-4A9BCE7E76FB"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapEncoderInfo : IWICBitmapCodecInfo + { + #region IWICBitmapCodecInfo + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion IWICComponentInfo + + new Guid GetContainerFormat(); + + new uint GetPixelFormats( + uint cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + Guid[] pguidPixelFormats + ); + + new uint GetColorManagementVersion( + uint cchColorManagementVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzColorManagementVersion + ); + + new uint GetDeviceManufacturer( + uint cchDeviceManufacturer, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceManufacturer + ); + + new uint GetDeviceModels( + uint cchDeviceModels, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceModels + ); + + new uint GetMimeTypes( + uint cchMimeTypes, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzMimeTypes + ); + + new uint GetFileExtensions( + uint cchFileExtensions, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFileExtensions + ); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportAnimation(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportChromakey(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportLossless(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportMultiframe(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool MatchesMimeType( + [MarshalAs(UnmanagedType.LPWStr)] + string wzMimeType + ); + #endregion IWICBitmapCodecInfo + + IWICBitmapEncoder CreateInstance(); + } + + [ComImport, Guid("D8CD007F-D08F-4191-9BFC-236EA7F0E4B5"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICBitmapDecoderInfo : IWICBitmapCodecInfo + { + #region IWICBitmapCodecInfo + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion IWICComponentInfo + + new Guid GetContainerFormat(); + + new uint GetPixelFormats( + uint cFormats, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + Guid[] pguidPixelFormats + ); + + new uint GetColorManagementVersion( + uint cchColorManagementVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzColorManagementVersion + ); + + new uint GetDeviceManufacturer( + uint cchDeviceManufacturer, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceManufacturer + ); + + new uint GetDeviceModels( + uint cchDeviceModels, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceModels + ); + + new uint GetMimeTypes( + uint cchMimeTypes, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzMimeTypes + ); + + new uint GetFileExtensions( + uint cchFileExtensions, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFileExtensions + ); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportAnimation(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportChromakey(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportLossless(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportMultiframe(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool MatchesMimeType( + [MarshalAs(UnmanagedType.LPWStr)] + string wzMimeType + ); + #endregion IWICBitmapCodecInfo + + uint GetPatterns( + uint cbSizePatterns, + IntPtr pPatterns, + out uint pcPatterns + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool MatchesPattern( + IStream pIStream + ); + + IWICBitmapDecoder CreateInstance(); + } + + [ComImport, Guid("E8EDA601-3D48-431a-AB44-69059BE88BBE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICPixelFormatInfo : IWICComponentInfo + { + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion IWICComponentInfo + + Guid GetFormatGUID(); + + IWICColorContext GetColorContext(); + + uint GetBitsPerPixel(); + + uint GetChannelCount(); + + uint GetChannelMask( + uint uiChannelIndex, + uint cbMaskBuffer, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] pbMaskBuffer + ); + } + + [ComImport, Guid("ec5ec8a9-c395-4314-9c77-54d7a935ff70"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICImagingFactory + { + IWICBitmapDecoder CreateDecoderFromFilename( + [MarshalAs(UnmanagedType.LPWStr)] + string wzFilename, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + GenericAccessRights dwDesiredAccess, + WICDecodeOptions metadataOptions + ); + + IWICBitmapDecoder CreateDecoderFromStream( + IStream pIStream, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + WICDecodeOptions metadataOptions + ); + + IWICBitmapDecoder CreateDecoderFromFileHandle( + IntPtr hFile, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + WICDecodeOptions metadataOptions + ); + + IWICComponentInfo CreateComponentInfo( + [MarshalAs(UnmanagedType.LPStruct)] + Guid clsidComponent + ); + + IWICBitmapDecoder CreateDecoder( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + + IWICBitmapEncoder CreateEncoder( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + + IWICPalette CreatePalette(); + + IWICFormatConverter CreateFormatConverter(); + + IWICBitmapScaler CreateBitmapScaler(); + + IWICBitmapClipper CreateBitmapClipper(); + + IWICBitmapFlipRotator CreateBitmapFlipRotator(); + + IWICStream CreateStream(); + + IWICColorContext CreateColorContext(); + + IWICColorTransform CreateColorTransform(); + + IWICBitmap CreateBitmap( + uint uiWidth, + uint uiHeight, + [MarshalAs(UnmanagedType.LPStruct)] + Guid pixelFormat, + WICBitmapCreateCacheOption option + ); + + IWICBitmap CreateBitmapFromSource( + IWICBitmapSource pIBitmapSource, + WICBitmapCreateCacheOption option + ); + + IWICBitmap CreateBitmapFromSourceRect( + IWICBitmapSource pIBitmapSource, + uint x, + uint y, + uint width, + uint height + ); + + IWICBitmap CreateBitmapFromMemory( + uint uiWidth, + uint uiHeight, + [MarshalAs(UnmanagedType.LPStruct)] + Guid pixelFormat, + uint cbStride, + uint cbBufferSize, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] + byte[] pbBuffer + ); + + IWICBitmap CreateBitmapFromHBITMAP( + IntPtr hBitmap, + IntPtr hPalette, + WICBitmapAlphaChannelOption options + ); + + IWICBitmap CreateBitmapFromHICON( + IntPtr hIcon + ); + + IEnumUnknown CreateComponentEnumerator( + WICComponentType componentTypes, + WICComponentEnumerateOptions options + ); + + IWICFastMetadataEncoder CreateFastMetadataEncoderFromDecoder( + IWICBitmapDecoder pIDecoder + ); + + IWICFastMetadataEncoder CreateFastMetadataEncoderFromFrameDecode( + IWICBitmapFrameDecode pIFrameDecoder + ); + + IWICMetadataQueryWriter CreateQueryWriter( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidMetadataFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + + IWICMetadataQueryWriter CreateQueryWriterFromReader( + IWICMetadataQueryReader pIQueryReader, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + } + + internal enum WICTiffCompressionOption : uint + { + WICTiffCompressionDontCare = 0x00000000, + WICTiffCompressionNone = 0x00000001, + WICTiffCompressionCCITT3 = 0x00000002, + WICTiffCompressionCCITT4 = 0x00000003, + WICTiffCompressionLZW = 0x00000004, + WICTiffCompressionRLE = 0x00000005, + WICTiffCompressionZIP = 0x00000006, + WICTiffCompressionLZWHDifferencing = 0x00000007, + } + + internal enum WICJpegYCrCbSubsamplingOption : uint + { + WICJpegYCrCbSubsamplingDefault = 0x00000000, + WICJpegYCrCbSubsampling420 = 0x00000001, + WICJpegYCrCbSubsampling422 = 0x00000002, + WICJpegYCrCbSubsampling444 = 0x00000003, + WICJpegYCrCbSubsampling440 = 0x00000004, + } + + [Flags] + internal enum WICNamedWhitePoint : uint + { + WICWhitePointDefault = 0x00000001, + WICWhitePointDaylight = 0x00000002, + WICWhitePointCloudy = 0x00000004, + WICWhitePointShade = 0x00000008, + WICWhitePointTungsten = 0x00000010, + WICWhitePointFluorescent = 0x00000020, + WICWhitePointFlash = 0x00000040, + WICWhitePointUnderwater = 0x00000080, + WICWhitePointCustom = 0x00000100, + WICWhitePointAutoWhiteBalance = 0x00000200, + WICWhitePointAsShot = WICWhitePointDefault, + } + + internal enum WICRawCapabilities : uint + { + WICRawCapabilityNotSupported = 0x00000000, + WICRawCapabilityGetSupported = 0x00000001, + WICRawCapabilityFullySupported = 0x00000002 + } + + internal enum WICRawRotationCapabilities : uint + { + WICRawRotationCapabilityNotSupported = 0x00000000, + WICRawRotationCapabilityGetSupported = 0x00000001, + WICRawRotationCapabilityNinetyDegreesSupported = 0x00000002, + WICRawRotationCapabilityFullySupported = 0x00000003 + } + + internal struct WICRawCapabilitiesInfo + { + public uint cbSize; + public uint CodecMajorVersion; + public uint CodecMinorVersion; + public WICRawCapabilities ExposureCompensationSupport; + public WICRawCapabilities ContrastSupport; + public WICRawCapabilities RGBWhitePointSupport; + public WICRawCapabilities NamedWhitePointSupport; + public WICNamedWhitePoint NamedWhitePointSupportMask; + public WICRawCapabilities KelvinWhitePointSupport; + public WICRawCapabilities GammaSupport; + public WICRawCapabilities TintSupport; + public WICRawCapabilities SaturationSupport; + public WICRawCapabilities SharpnessSupport; + public WICRawCapabilities NoiseReductionSupport; + public WICRawCapabilities DestinationColorProfileSupport; + public WICRawCapabilities ToneCurveSupport; + public WICRawRotationCapabilities RotationSupport; + public WICRawCapabilities RenderModeSupport; + } + + internal enum WICRawParameterSet : uint + { + WICAsShotParameterSet = 0x00000001, + WICUserAdjustedParameterSet = 0x00000002, + WICAutoAdjustedParameterSet = 0x00000003, + } + + internal enum WICRawRenderMode : uint + { + WICRawRenderModeDraft = 0x00000001, + WICRawRenderModeNormal = 0x00000002, + WICRawRenderModeBestQuality = 0x00000003 + } + + internal struct WICRawToneCurvePoint + { + public double Input; + public double Output; + } + + internal struct WICRawToneCurve + { + public uint cPoints; + public WICRawToneCurvePoint[] aPoints; + } + + [Flags] + internal enum WICRawChangeNotification : uint + { + WICRawChangeNotification_ExposureCompensation = 0x00000001, + WICRawChangeNotification_NamedWhitePoint = 0x00000002, + WICRawChangeNotification_KelvinWhitePoint = 0x00000004, + WICRawChangeNotification_RGBWhitePoint = 0x00000008, + WICRawChangeNotification_Contrast = 0x00000010, + WICRawChangeNotification_Gamma = 0x00000020, + WICRawChangeNotification_Sharpness = 0x00000040, + WICRawChangeNotification_Saturation = 0x00000080, + WICRawChangeNotification_Tint = 0x00000100, + WICRawChangeNotification_NoiseReduction = 0x00000200, + WICRawChangeNotification_DestinationColorContext = 0x00000400, + WICRawChangeNotification_ToneCurve = 0x00000800, + WICRawChangeNotification_Rotation = 0x00001000, + WICRawChangeNotification_RenderMode = 0x00002000 + } + + [ComImport, Guid("95c75a6e-3e8c-4ec2-85a8-aebcc551e59b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICDevelopRawNotificationCallback + { + void Notify(WICRawChangeNotification NotificationMask); + } + + [ComImport, Guid("fbec5e44-f7be-4b65-b7f8-c0c81fef026d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICDevelopRaw : IWICBitmapFrameDecode + { + #region IWICBitmapFrameDecode + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + new IWICMetadataQueryReader GetMetadataQueryReader(); + + new uint GetColorContexts( + uint cCount, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + IWICColorContext[] pIColorContexts + ); + + new IWICBitmapSource GetThumbnail(); + #endregion IWICBitmapFrameDecode + + void QueryRawCapabilitiesInfo( + ref WICRawCapabilitiesInfo pInfo + ); + + void LoadParameterSet( + WICRawParameterSet ParameterSet + ); + + IPropertyBag2 GetCurrentParameterSet(); + + void SetExposureCompensation( + double ev + ); + + double GetExposureCompensation(); + + void SetWhitePointRGB( + uint Red, + uint Green, + uint Blue + ); + + void GetWhitePointRGB( + out uint pRed, + out uint pGreen, + out uint pBlue + ); + + void SetNamedWhitePoint( + WICNamedWhitePoint WhitePoint + ); + + WICNamedWhitePoint GetNamedWhitePoint(); + + void SetWhitePointKelvin( + uint WhitePointKelvin + ); + + uint GetWhitePointKelvin(); + + void GetKelvinRangeInfo( + out uint pMinKelvinTemp, + out uint pMaxKelvinTemp, + out uint pKelvinTempStepValue + ); + + void SetContrast( + double Contrast + ); + + double GetContrast(); + + void SetGamma( + double Gamma + ); + + double GetGamma(); + + void SetSharpness( + double Sharpness + ); + + double GetSharpness(); + + void SetSaturation( + double Saturation + ); + + double GetSaturation(); + + void SetTint( + double Tint + ); + + double GetTint(); + + void SetNoiseReduction( + double NoiseReduction + ); + + double GetNoiseReduction(); + + void SetDestinationColorContext( + IWICColorContext pColorContext + ); + + void SetToneCurve( + uint cbToneCurveSize, + IntPtr pToneCurve + ); + + uint GetToneCurve( + uint cbToneCurveBufferSize, + IntPtr pToneCurve + ); + + void SetRotation( + double Rotation + ); + + double GetRotation(); + + void SetRenderMode( + WICRawRenderMode RenderMode + ); + + WICRawRenderMode GetRenderMode(); + + void SetNotificationCallback( + IWICDevelopRawNotificationCallback pCallback + ); + } + + [ComImport, Guid("CACAF262-9370-4615-A13B-9F5539DA4C0A")] + internal class WICImagingFactory { } + + [ComImport, Guid("317D06E8-5F24-433D-BDF7-79CE68D8ABC2")] + internal class WICImagingFactory2 { } + + internal enum WIC8BIMIPTCProperties : uint + { + WIC8BIMIPTCPString = 0x00000001, + WIC8BIMIPTCEmbeddedIPTC = 0x00000002 + } + + internal enum WIC8BIMResolutionInfoProperties : uint + { + WIC8BIMResolutionInfoPString = 0x00000001, + WIC8BIMResolutionInfoHResolution = 0x00000002, + WIC8BIMResolutionInfoHResolutionUnit = 0x00000003, + WIC8BIMResolutionInfoWidthUnit = 0x00000004, + WIC8BIMResolutionInfoVResolution = 0x00000005, + WIC8BIMResolutionInfoVResolutionUnit = 0x00000006, + WIC8BIMResolutionInfoHeightUnit = 0x00000007 + } + + internal enum WICPngFilterOption : uint + { + WICPngFilterUnspecified = 0x00000000, + WICPngFilterNone, + WICPngFilterSub, + WICPngFilterUp, + WICPngFilterAverage, + WICPngFilterPaeth, + WICPngFilterAdaptive + } + + internal enum WICGifLogicalScreenDescriptorProperties : uint + { + WICGifLogicalScreenSignature = 0x0001, + WICGifLogicalScreenDescriptorWidth, + WICGifLogicalScreenDescriptorHeight, + WICGifLogicalScreenDescriptorGlobalColorTableFlag, + WICGifLogicalScreenDescriptorColorResolution, + WICGifLogicalScreenDescriptorSortFlag, + WICGifLogicalScreenDescriptorGlobalColorTableSize, + WICGifLogicalScreenDescriptorBackgroundColorIndex, + WICGifLogicalScreenDescriptorPixelAspectRatio, + WICGifLogicalScreenDescriptorMax + } + + internal enum WICGifImageDescriptorProperties : uint + { + WICGifImageDescriptorLeft = 0x0001, + WICGifImageDescriptorTop, + WICGifImageDescriptorWidth, + WICGifImageDescriptorHeight, + WICGifImageDescriptorLocalColorTableFlag, + WICGifImageDescriptorInterlaceFlag, + WICGifImageDescriptorSortFlag, + WICGifImageDescriptorLocalColorTableSize, + WICGifImageDescriptorMax + } + + internal enum WICGifGraphicControlExtensionProperties : uint + { + WICGifGraphicControlExtensionDisposal = 0x0001, + WICGifGraphicControlExtensionUserInputFlag, + WICGifGraphicControlExtensionTransparencyFlag, + WICGifGraphicControlExtensionDelay, + WICGifGraphicControlExtensionTransparentColorIndex, + WICGifGraphicControlExtensionMax + } + + internal enum WICGifApplicationExtensionProperties : uint + { + WICGifApplicationExtensionApplication = 0x0001, + WICGifApplciationExtensionData, + WICGifApplciationExtensionMax + } + + [ComImport, Guid("DAAC296F-7AA5-4dbf-8D15-225C5976F891"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICProgressiveLevelControl + { + uint GetLevelCount(); + uint GetCurrentLevel(); + void SetCurrentLevel(uint nLevel); + } + + internal enum WICSectionAccessLevel : uint + { + WICSectionAccessLevelRead = 0x00000001, + WICSectionAccessLevelReadWrite = 0x00000003 + } + + internal enum WICPixelFormatNumericRepresentation : int + { + WICPixelFormatNumericRepresentationUnspecified = 0x00000000, + WICPixelFormatNumericRepresentationIndexed = 0x00000001, + WICPixelFormatNumericRepresentationUnsignedInteger = 0x00000002, + WICPixelFormatNumericRepresentationSignedInteger = 0x00000003, + WICPixelFormatNumericRepresentationFixed = 0x00000004, + WICPixelFormatNumericRepresentationFloat = 0x00000005 + } + + [ComImport, Guid("A9DB33A2-AF5F-43C7-B679-74F5984B5AA4"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICPixelFormatInfo2 : IWICPixelFormatInfo + { + #region IWICPixelFormatInfo + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion IWICComponentInfo + + new Guid GetFormatGUID(); + + new IWICColorContext GetColorContext(); + + new uint GetBitsPerPixel(); + + new uint GetChannelCount(); + + new uint GetChannelMask( + uint uiChannelIndex, + uint cbMaskBuffer, + [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] pbMaskBuffer + ); + #endregion IWICPixelFormatInfo + + [return: MarshalAs(UnmanagedType.Bool)] + bool SupportsTransparency(); + + WICPixelFormatNumericRepresentation GetNumericRepresentation(); + } + + internal enum WICPlanarOptions : uint + { + WICPlanarOptionsDefault = 0x00000000, + WICPlanarOptionsPreserveSubsampling = 0x00000001, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WICBitmapPlaneDescription + { + public Guid Format; + public uint Width; + public uint Height; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WICBitmapPlane + { + public Guid Format; + public IntPtr pbBuffer; + public uint cbStride; + public uint cbBufferSize; + } + + [ComImport, Guid("BEBEE9CB-83B0-4DCC-8132-B0AAA55EAC96"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICPlanarFormatConverter : IWICBitmapSource + { + #region IWICBitmapSource + new void GetSize( + out uint puiWidth, + out uint puiHeight + ); + + new Guid GetPixelFormat(); + + new void GetResolution( + out double pDpiX, + out double pDpiY + ); + + new void CopyPalette( + IWICPalette pIPalette + ); + + new void CopyPixels( + WICRect prc, + uint cbStride, + uint cbBufferSize, + IntPtr pbBuffer + ); + #endregion IWICBitmapSource + + void Initialize( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + IWICBitmapSource[] ppPlanes, + uint cPlanes, + [MarshalAs(UnmanagedType.LPStruct)] + Guid dstFormat, + WICBitmapDitherType dither, + IWICPalette pIPalette, + double alphaThresholdPercent, + WICBitmapPaletteType paletteTranslate + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool CanConvert( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + Guid[] pSrcPixelFormats, + uint cSrcPlanes, + [MarshalAs(UnmanagedType.LPStruct)] + Guid dstPixelFormat + ); + } + + [ComImport, Guid("F928B7B8-2221-40C1-B72E-7E82F1974D1A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICPlanarBitmapFrameEncode + { + void WritePixels( + uint lineCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + WICBitmapPlane[] pPlanes, + uint cPlanes + ); + + void WriteSource( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + IWICBitmapSource[] ppPlanes, + uint cPlanes, + WICRect prcSource + ); + } + + [ComImport, Guid("3AFF9CCE-BE95-4303-B927-E7D16FF4A613"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICPlanarBitmapSourceTransform + { + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportTransform( + ref uint puiWidth, + ref uint puiHeight, + WICBitmapTransformOptions dstTransform, + WICPlanarOptions dstPlanarOptions, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)] + Guid[] pguidDstFormats, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)] + WICBitmapPlaneDescription[] pPlaneDescriptions, + uint cPlanes + ); + + void CopyPixels( + WICRect prcSource, + uint uiWidth, + uint uiHeight, + WICBitmapTransformOptions dstTransform, + WICPlanarOptions dstPlanarOptions, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)] + WICBitmapPlane[] pDstPlanes, + uint cPlanes + ); + } + + internal enum WICJpegIndexingOptions : uint + { + WICJpegIndexingOptionsGenerateOnDemand = 0x00000000, + WICJpegIndexingOptionsGenerateOnLoad= 0x00000001, + } + + internal enum WICJpegTransferMatrix : uint + { + WICJpegTransferMatrixIdentity = 0x00000000, + WICJpegTransferMatrixBT601 = 0x00000001, + } + + internal enum WICJpegScanType : uint + { + WICJpegScanTypeInterleaved = 0x00000000, + WICJpegScanTypePlanarComponents = 0x00000001, + WICJpegScanTypeProgressive = 0x00000002, + } + + internal enum WICJpegSampleFactors : uint + { + WICJpegSampleFactorsOne = 0x00000011, + WICJpegSampleFactorsThree420 = 0x00111122, + WICJpegSampleFactorsThree422 = 0x00111121, + WICJpegSampleFactorsThree440 = 0x00111112, + WICJpegSampleFactorsThree444 = 0x00111111, + } + + internal enum WICJpegQuantizationTableIndices : uint + { + WICJpegQuantizationBaselineOne = 0x00000000, + WICJpegQuantizationBaselineThree = 0x00010100, + } + + internal enum WICJpegHuffmanTableIndices : uint + { + WICJpegHuffmanBaselineOne = 0x00000000, + WICJpegHuffmanBaselineThree = 0x00111100, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WICBitmapPlanes + { + public Guid Format; + public IntPtr pbBuffer; + public uint cbStride; + public uint cbBufferSize; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WICJpegFrameHeader + { + public uint Width; + public uint Height; + public WICJpegTransferMatrix TransferMatrix; + public WICJpegScanType ScanType; + public uint cComponents; + public uint ComponentIdentifiers; + public WICJpegSampleFactors SampleFactors; + public WICJpegQuantizationTableIndices QuantizationTableIndices; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct WICJpegScanHeader + { + public uint cComponents; + public uint RestartInterval; + public uint ComponentSelectors; + public WICJpegHuffmanTableIndices HuffmanTableIndices; + public byte StartSpectralSelection; + public byte EndSpectralSelection; + public byte SuccessiveApproximationHigh; + public byte SuccessiveApproximationLow; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct WICJpegAcHuffmanTable + { + public fixed byte CodeCounts[16]; + public fixed byte CodeValues[162]; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct WICJpegDcHuffmanTable + { + public fixed byte CodeCounts[12]; + public fixed byte CodeValues[12]; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct WICJpegQuantizationTable + { + public fixed byte Elements[64]; + } + + [ComImport, Guid("8939F66E-C46A-4c21-A9D1-98B327CE1679"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICJpegFrameDecode + { + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportIndexing(); + + void SetIndexing( + WICJpegIndexingOptions options, + uint horizontalIntervalSize + ); + + void ClearIndexing(); + + WICJpegAcHuffmanTable GetAcHuffmanTable( + uint scanIndex, + uint tableIndex + ); + + WICJpegDcHuffmanTable GetDcHuffmanTable( + uint scanIndex, + uint tableIndex + ); + + WICJpegQuantizationTable GetQuantizationTable( + uint scanIndex, + uint tableIndex + ); + + WICJpegFrameHeader GetFrameHeader(); + + WICJpegScanHeader GetScanHeader( + uint scanIndex + ); + + uint CopyScan( + uint scanIndex, + uint scanOffset, + uint cbScanData, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + byte[] pbScanData + ); + + uint CopyMinimalStream( + uint streamOffset, + uint cbStreamData, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + byte[] pbStreamData + ); + } + + [ComImport, Guid("2F0C601F-D2C6-468C-ABFA-49495D983ED1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICJpegFrameEncode + { + WICJpegAcHuffmanTable GetAcHuffmanTable( + uint scanIndex, + uint tableIndex + ); + + WICJpegDcHuffmanTable GetDcHuffmanTable( + uint scanIndex, + uint tableIndex + ); + + WICJpegQuantizationTable GetQuantizationTable( + uint scanIndex, + uint tableIndex + ); + + void WriteScan( + uint cbScanData, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + byte[] pbScanData + ); + } +} diff --git a/src/Interop/WinCodecExtensions.cs b/src/Interop/WinCodecExtensions.cs new file mode 100644 index 00000000..28969d04 --- /dev/null +++ b/src/Interop/WinCodecExtensions.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.InteropServices; + +namespace PhotoSauce.MagicScaler.Interop +{ + internal static class WinCodecExtensions + { + [DllImport("WindowsCodecs", EntryPoint = "IWICBitmapFrameDecode_GetColorContexts_Proxy")] + private extern static int GetColorContexts(IWICBitmapFrameDecode THIS_PTR, uint cCount, IntPtr[] ppIColorContexts, out uint pcActualCount); + + [DllImport("WindowsCodecs", EntryPoint = "IWICBitmapFrameDecode_GetMetadataQueryReader_Proxy")] + private extern static int GetMetadataQueryReader(IWICBitmapFrameDecode THIS_PTR, out IWICMetadataQueryReader ppIMetadataQueryReader); + + [DllImport("WindowsCodecs", EntryPoint = "IWICBitmapFrameEncode_GetMetadataQueryWriter_Proxy")] + private extern static int GetMetadataQueryWriter(IWICBitmapFrameEncode THIS_PTR, out IWICMetadataQueryWriter ppIMetadataQueryWriter); + + [DllImport("WindowsCodecs", EntryPoint = "IWICMetadataQueryReader_GetMetadataByName_Proxy")] + private extern static int GetMetadataByName(IWICMetadataQueryReader THIS_PTR, [MarshalAs(UnmanagedType.LPWStr)]string wzName, IntPtr pvarValue); + + [DllImport("WindowsCodecs", EntryPoint = "IWICMetadataQueryWriter_SetMetadataByName_Proxy")] + private extern static int SetMetadataByName(IWICMetadataQueryWriter THIS_PTR, [MarshalAs(UnmanagedType.LPWStr)]string wzName, [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))]PropVariant pvarValue); + + public static uint GetColorContextCount(this IWICBitmapFrameDecode frame) + { + uint ccc; + int hr = GetColorContexts(frame, 0, null, out ccc); + return hr >= 0 ? ccc : 0u; + } + + public static IWICMetadataQueryReader GetMetadataQueryReaderNoThrow(this IWICBitmapFrameDecode frame) + { + IWICMetadataQueryReader rdr; + int hr = GetMetadataQueryReader(frame, out rdr); + return hr >= 0 ? rdr : null; + } + + public static IWICMetadataQueryWriter GetMetadataQueryWriterNoThrow(this IWICBitmapFrameEncode frame) + { + IWICMetadataQueryWriter wri; + int hr = GetMetadataQueryWriter(frame, out wri); + return hr >= 0 ? wri : null; + } + + public static bool HasMetadataName(this IWICMetadataQueryReader meta, string name) + { + int hr = GetMetadataByName(meta, name, IntPtr.Zero); + return hr >= 0; + } + + public static bool SetMetadataByNameNoThrow(this IWICMetadataQueryWriter meta, string name, PropVariant value) + { + int hr = SetMetadataByName(meta, name, value); + return hr >= 0; + } + } +} diff --git a/src/Interop/WinCodecSdk.cs b/src/Interop/WinCodecSdk.cs new file mode 100644 index 00000000..3ace7125 --- /dev/null +++ b/src/Interop/WinCodecSdk.cs @@ -0,0 +1,621 @@ +// This file was originally part of WIC Tools, published under the Ms-PL license +// https://web.archive.org/web/20101223145710/http://code.msdn.microsoft.com/wictools/Project/License.aspx +// It has been modified from its original version. Changes copyright Clinton Ingram. + +//---------------------------------------------------------------------------------------- +// THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//---------------------------------------------------------------------------------------- + +using System; +using System.Text; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +namespace PhotoSauce.MagicScaler.Interop +{ + internal enum WICMetadataCreationOptions : uint + { + WICMetadataCreationDefault = 0x00000000, + WICMetadataCreationAllowUnknown = WICMetadataCreationDefault, + WICMetadataCreationFailUnknown = 0x00010000, + WICMetadataCreationMask = 0xFFFF0000 + } + + internal enum WICPersistOptions : uint + { + WICPersistOptionDefault = 0x00000000, + WICPersistOptionLittleEndian = 0x00000000, + WICPersistOptionBigEndian = 0x00000001, + WICPersistOptionStrictFormat = 0x00000002, + WICPersistOptionNoCacheStream = 0x00000004, + WICPersistOptionPreferUTF8 = 0x00000008, + WICPersistOptionMask = 0x0000FFFF + } + + [ComImport, Guid("FEAA2A8D-B3F3-43E4-B25C-D1DE990A1AE1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataBlockReader + { + Guid GetContainerFormat(); + + uint GetCount(); + + IWICMetadataReader GetReaderByIndex( + uint nIndex + ); + + IEnumUnknown GetEnumerator(); + } + + [ComImport, Guid("08FB9676-B444-41E8-8DBE-6A53A542BFF1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataBlockWriter : IWICMetadataBlockReader + { + #region IWICMetadataBlockReader + new Guid GetContainerFormat(); + + new uint GetCount(); + + new IWICMetadataReader GetReaderByIndex( + uint nIndex + ); + + new IEnumUnknown GetEnumerator(); + #endregion + + void InitializeFromBlockReader( + IWICMetadataBlockReader pIMDBlockReader + ); + + IWICMetadataWriter GetWriterByIndex( + uint nIndex + ); + + void AddWriter( + IWICMetadataWriter pIMetadataWriter + ); + + void SetWriterByIndex( + uint nIndex, + IWICMetadataWriter pIMetadataWriter + ); + + void RemoveWriterByIndex( + uint nIndex + ); + } + + [ComImport, Guid("9204FE99-D8FC-4FD5-A001-9536B067A899"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataReader + { + Guid GetMetadataFormat(); + + IWICMetadataHandlerInfo GetMetadataHandlerInfo(); + + uint GetCount(); + + void GetValueByIndex( + uint nIndex, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarSchema, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarId, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + void GetValue( + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarSchema, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarId, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + IWICEnumMetadataItem GetEnumerator(); + } + + [ComImport, Guid("F7836E16-3BE0-470B-86BB-160D0AECD7DE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataWriter : IWICMetadataReader + { + #region IWICMetadataReader + new Guid GetMetadataFormat(); + + new IWICMetadataHandlerInfo GetMetadataHandlerInfo(); + + new uint GetCount(); + + new void GetValueByIndex(uint nIndex, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarSchema, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarId, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + new void GetValue( + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarSchema, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarId, + [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + new IWICEnumMetadataItem GetEnumerator(); + #endregion + + void SetValue( + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarSchema, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarId, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + void SetValueByIndex( + uint nIndex, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarSchema, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarId, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarValue + ); + + void RemoveValue( + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarSchema, + [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PropVariant.Marshaler))] + PropVariant pvarId + ); + + void RemoveValueByIndex( + uint nIndex + ); + } + + [ComImport, Guid("449494BC-B468-4927-96D7-BA90D31AB505"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICStreamProvider + { + IStream GetStream(); + + uint GetPersistOptions(); + + Guid GetPreferredVendorGUID(); + + void RefreshStream(); + } + + [ComImport, Guid("ABA958BF-C672-44D1-8D61-CE6DF2E682C2"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataHandlerInfo : IWICComponentInfo + { + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion + + Guid GetMetadataFormat(); + + uint GetContainerFormats( + uint cContainerFormats, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + Guid[] pguidContainerFormats + ); + + uint GetDeviceManufacturer( + uint cchDeviceManufacturer, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceManufacturer + ); + + uint GetDeviceModels( + uint cchDeviceModels, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceModels + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesRequireFullStream(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesSupportPadding(); + + [return: MarshalAs(UnmanagedType.Bool)] + bool DoesRequireFixedSize(); + } + + internal struct WICMetadataPattern + { + public ulong Position; + public uint Length; + public IntPtr Pattern; + public IntPtr Mask; + public ulong DataOffset; + } + + [ComImport, Guid("EEBF1F5B-07C1-4447-A3AB-22ACAF78A804"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataReaderInfo : IWICMetadataHandlerInfo + { + #region IWICMetadataHandlerInfo + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion + + new Guid GetMetadataFormat(); + + new uint GetContainerFormats( + uint cContainerFormats, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + Guid[] pguidContainerFormats + ); + + new uint GetDeviceManufacturer( + uint cchDeviceManufacturer, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceManufacturer + ); + + new uint GetDeviceModels( + uint cchDeviceModels, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceModels + ); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesRequireFullStream(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportPadding(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesRequireFixedSize(); + #endregion + + uint GetPatterns( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + uint cbSize, + IntPtr pPattern, + out uint pcCount + ); + + [return: MarshalAs(UnmanagedType.Bool)] + bool MatchesPattern( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + IStream pIStream + ); + + IWICMetadataReader CreateInstance(); + } + + internal struct WICMetadataHeader + { + public ulong Position; + public uint Length; + public IntPtr Header; + public ulong DataOffset; + } + + [ComImport, Guid("B22E3FBA-3925-4323-B5C1-9EBFC430F236"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICMetadataWriterInfo : IWICMetadataHandlerInfo + { + #region IWICMetadataHandlerInfo + #region IWICComponentInfo + new WICComponentType GetComponentType(); + + new Guid GetCLSID(); + + new WICComponentSigning GetSigningStatus(); + + new uint GetAuthor( + uint cchAuthor, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzAuthor + ); + + new Guid GetVendorGUID(); + + new uint GetVersion( + uint cchVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzVersion + ); + + new uint GetSpecVersion( + uint cchSpecVersion, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzSpecVersion + ); + + new uint GetFriendlyName( + uint cchFriendlyName, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzFriendlyName + ); + #endregion + + new Guid GetMetadataFormat(); + + new uint GetContainerFormats( + uint cContainerFormats, + [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] + Guid[] pguidContainerFormats + ); + + new uint GetDeviceManufacturer( + uint cchDeviceManufacturer, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceManufacturer + ); + + new uint GetDeviceModels( + uint cchDeviceModels, + [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] + StringBuilder wzDeviceModels + ); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesRequireFullStream(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesSupportPadding(); + + [return: MarshalAs(UnmanagedType.Bool)] + new bool DoesRequireFixedSize(); + #endregion + + uint GetHeader( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + uint cbSize, + IntPtr pHeader + ); + + IWICMetadataWriter CreateInstance(); + } + + [ComImport, Guid("412D0C3A-9650-44FA-AF5B-DD2A06C8E8FB"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IWICComponentFactory : IWICImagingFactory + { + #region IWICImagingFactory + new IWICBitmapDecoder CreateDecoderFromFilename( + [MarshalAs(UnmanagedType.LPWStr)] + string wzFilename, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + GenericAccessRights dwDesiredAccess, + WICDecodeOptions metadataOptions + ); + + new IWICBitmapDecoder CreateDecoderFromStream( + IStream pIStream, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + WICDecodeOptions metadataOptions + ); + + new IWICBitmapDecoder CreateDecoderFromFileHandle( + IntPtr hFile, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + WICDecodeOptions metadataOptions + ); + + new IWICComponentInfo CreateComponentInfo( + [MarshalAs(UnmanagedType.LPStruct)] + Guid clsidComponent + ); + + new IWICBitmapDecoder CreateDecoder( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + + new IWICBitmapEncoder CreateEncoder( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + + new IWICPalette CreatePalette(); + + new IWICFormatConverter CreateFormatConverter(); + + new IWICBitmapScaler CreateBitmapScaler(); + + new IWICBitmapClipper CreateBitmapClipper(); + + new IWICBitmapFlipRotator CreateBitmapFlipRotator(); + + new IWICStream CreateStream(); + + new IWICColorContext CreateColorContext(); + + new IWICColorTransform CreateColorTransform(); + + new IWICBitmap CreateBitmap( + uint uiWidth, + uint uiHeight, + [MarshalAs(UnmanagedType.LPStruct)] + Guid pixelFormat, + WICBitmapCreateCacheOption option + ); + + new IWICBitmap CreateBitmapFromSource( + IWICBitmapSource pIBitmapSource, + WICBitmapCreateCacheOption option + ); + + new IWICBitmap CreateBitmapFromSourceRect( + IWICBitmapSource pIBitmapSource, + uint x, + uint y, + uint width, + uint height + ); + + new IWICBitmap CreateBitmapFromMemory( + uint uiWidth, + uint uiHeight, + [MarshalAs(UnmanagedType.LPStruct)] + Guid pixelFormat, + uint cbStride, + uint cbBufferSize, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] + byte[] pbBuffer + ); + + new IWICBitmap CreateBitmapFromHBITMAP( + IntPtr hBitmap, + IntPtr hPalette, + WICBitmapAlphaChannelOption options + ); + + new IWICBitmap CreateBitmapFromHICON( + IntPtr hIcon + ); + + new IEnumUnknown CreateComponentEnumerator( + WICComponentType componentTypes, /* WICComponentType */ + WICComponentEnumerateOptions options /* WICComponentEnumerateOptions */ + ); + + new IWICFastMetadataEncoder CreateFastMetadataEncoderFromDecoder( + IWICBitmapDecoder pIDecoder + ); + + new IWICFastMetadataEncoder CreateFastMetadataEncoderFromFrameDecode( + IWICBitmapFrameDecode pIFrameDecoder + ); + + new IWICMetadataQueryWriter CreateQueryWriter( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidMetadataFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + + new IWICMetadataQueryWriter CreateQueryWriterFromReader( + IWICMetadataQueryReader pIQueryReader, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + #endregion + + IWICMetadataReader CreateMetadataReader( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidMetadataFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + uint dwOptions, + IStream pIStream + ); + + IWICMetadataReader CreateMetadataReaderFromContainer( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidContainerFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + uint dwOptions, + IStream pIStream + ); + + IWICMetadataWriter CreateMetadataWriter( + [MarshalAs(UnmanagedType.LPStruct)] + Guid guidMetadataFormat, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor, + uint dwMetadataOptions + ); + + IWICMetadataWriter CreateMetadataWriterFromReader( + IWICMetadataReader pIReader, + [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] + Guid[] pguidVendor + ); + + IWICMetadataQueryReader CreateQueryReaderFromBlockReader( + IWICMetadataBlockReader pIBlockReader + ); + + IWICMetadataQueryWriter CreateQueryWriterFromBlockWriter( + IWICMetadataBlockWriter pIBlockWriter + ); + + IPropertyBag2 CreateEncoderPropertyBag( + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] + PROPBAG2[] ppropOptions, + uint cCount + ); + } +} \ No newline at end of file diff --git a/src/Interop/license b/src/Interop/license new file mode 100644 index 00000000..e84a61aa --- /dev/null +++ b/src/Interop/license @@ -0,0 +1,31 @@ +MICROSOFT PUBLIC LICENSE (Ms-PL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the software. + +A "contributor" is any person that distributes its contribution under this license. + +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. diff --git a/src/MagicScaler.csproj b/src/MagicScaler.csproj new file mode 100644 index 00000000..926b5b94 --- /dev/null +++ b/src/MagicScaler.csproj @@ -0,0 +1,174 @@ + + + + + Release + AnyCPU + {CB811C9F-8676-4207-AD89-13E784FD96DE} + Library + Properties + PhotoSauce.MagicScaler + PhotoSauce.MagicScaler + v4.6 + 512 + 1 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + True + False + True + False + False + False + True + True + True + True + True + True + True + True + False + True + False + True + False + False + False + False + True + False + True + True + True + False + False + + + + + + + + True + False + False + True + Full + %28none%29 + 0 + 6 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + True + False + True + False + False + False + True + True + True + True + True + True + True + True + False + True + False + True + False + False + False + False + True + False + True + True + True + False + False + + + + + + + + True + False + False + True + Full + DoNotBuild + 0 + 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..dcdcff58 --- /dev/null +++ b/src/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("MagicScaler")] +[assembly: AssemblyDescription("MagicScaler Core Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("PhotoSauce")] +[assembly: AssemblyProduct("MagicScaler")] +[assembly: AssemblyCopyright("Copyright © Clinton Ingram 2015-2017")] +[assembly: AssemblyCulture("")] + +[assembly: AssemblyVersion("0.6.0.0")] +[assembly: AssemblyFileVersion("0.6.0.0")] + +[assembly: ComVisible(false)] + +[assembly: InternalsVisibleTo("PhotoSauce.MagicScaler.Test")] +[assembly: InternalsVisibleTo("PhotoSauce.WebRSize")] +[assembly: InternalsVisibleTo("PhotoSauce.WebRSize.Test")] +[assembly: InternalsVisibleTo("VisualTest")] +[assembly: InternalsVisibleTo("MagicCLI")] diff --git a/src/Utilities/CacheHash.cs b/src/Utilities/CacheHash.cs new file mode 100644 index 00000000..b88232ca --- /dev/null +++ b/src/Utilities/CacheHash.cs @@ -0,0 +1,46 @@ +using System.IO; +using System.Text; +using System.Security.Cryptography; + +namespace PhotoSauce.MagicScaler +{ + internal static class CacheHash + { + private static readonly char[] Base32Table = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','2','3','4','5','6','7'}; + + private static string encode(byte[] bhash) + { + // first 40 bits from the crypto hash, base32 encoded + // https://tools.ietf.org/html/rfc4648#section-6 + char[] hash = new char[8], b32 = Base32Table; + hash[0] = b32[ bhash[0] >> 3]; + hash[1] = b32[((bhash[0] & 0x07) << 2) | (bhash[1] >> 6)]; + hash[2] = b32[( bhash[1] & 0x3e) >> 1]; + hash[3] = b32[((bhash[1] & 0x01) << 4) | (bhash[2] >> 4)]; + hash[4] = b32[((bhash[2] & 0x0f) << 1) | (bhash[3] >> 7)]; + hash[5] = b32[( bhash[3] & 0x7c) >> 2]; + hash[6] = b32[((bhash[3] & 0x03) << 3) | (bhash[4] >> 5)]; + hash[7] = b32[ bhash[4] & 0x1f]; + + return new string(hash); + } + + public static string Create(string data) + { + return Create(Encoding.UTF8.GetBytes(data)); + } + + public static string Create(byte[] data) + { + using (var hf = new SHA256Cng()) + return encode(hf.ComputeHash(data)); + } + + public static string Create(Stream data) + { + using (var hf = new SHA256Cng()) + return encode(hf.ComputeHash(data)); + } + } +} diff --git a/src/Utilities/MiscExtensions.cs b/src/Utilities/MiscExtensions.cs new file mode 100644 index 00000000..5b9f85c7 --- /dev/null +++ b/src/Utilities/MiscExtensions.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Configuration; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace PhotoSauce.MagicScaler +{ + internal static class MiscExtensions + { + public static TValue GetValueOrDefault(this IDictionary dic, TKey key, Func valueFactory = null) + { + TValue value; + return dic.TryGetValue(key, out value) ? value : valueFactory == null ? default(TValue) : valueFactory(); + } + + public static IDictionary ToDictionary(this NameValueCollection nv) + { + return nv.AllKeys.Where(k => !string.IsNullOrEmpty(k)).ToDictionary(k => k, k => nv.GetValues(k).LastOrDefault(), StringComparer.OrdinalIgnoreCase); + } + + public static IDictionary ToDictionary(this KeyValueConfigurationCollection kv) + { + return kv.AllKeys.Where(k => !string.IsNullOrEmpty(k)).ToDictionary(k => k, k => kv[k].Value, StringComparer.OrdinalIgnoreCase); + } + + public static IDictionary Coalesce(this IDictionary dic1, IDictionary dic2) + { + dic2.ToList().ForEach(i => dic1[i.Key] = i.Value); + return dic1; + } + } +} diff --git a/src/WIC/Generated/Convolver.cs b/src/WIC/Generated/Convolver.cs new file mode 100644 index 00000000..e7c5bc7d --- /dev/null +++ b/src/WIC/Generated/Convolver.cs @@ -0,0 +1,1057 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; + +using static PhotoSauce.MagicScaler.MathUtil; + +namespace PhotoSauce.MagicScaler +{ + unsafe internal class ConvolverBgra8bpc : IConvolver8bpc + { + private const int Channels = 4; + + void IConvolver8bpc.ConvolveSourceLine(byte* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx) + { + int* pmapx = mapxstart; + int* pmapxa = mapxastart + 1; + int* tp = tstart; + int* tpe = tstart + tlen; + + while (tp < tpe) + { + int a0 = 0, a1 = 0, a2 = 0, a3 = 0, aw = 0; + + int ix = *pmapx++; + byte* ip = istart + ix * Channels + 4 * Channels; + byte* ipe = ip + smapx * Channels - 3 * Channels; + int* mp = pmapx + 4, mpa = pmapxa + 4; + pmapx += smapx; + pmapxa += smapx + 1; + + while (ip < ipe) + { + int alpha = ip[-13]; + if (alpha == 0) + { + aw += mp[-4]; + } + else + { + int w = mp[-4]; + a0 += ip[-16] * w; + a1 += ip[-15] * w; + a2 += ip[-14] * w; + a3 += alpha * mpa[-4]; + } + + alpha = ip[-9]; + if (alpha == 0) + { + aw += mp[-3]; + } + else + { + int w = mp[-3]; + a0 += ip[-12] * w; + a1 += ip[-11] * w; + a2 += ip[-10] * w; + a3 += alpha * mpa[-3]; + } + + alpha = ip[-5]; + if (alpha == 0) + { + aw += mp[-2]; + } + else + { + int w = mp[-2]; + a0 += ip[-8] * w; + a1 += ip[-7] * w; + a2 += ip[-6] * w; + a3 += alpha * mpa[-2]; + } + + alpha = ip[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += ip[-4] * w; + a1 += ip[-3] * w; + a2 += ip[-2] * w; + a3 += alpha * mpa[-1]; + } + + ip += 4 * Channels; + mp += 4; + mpa+= 4; + } + + ip -= 3 * Channels; + mp -= 3; + mpa-= 3; + while (ip < ipe) + { + int alpha = ip[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += ip[-4] * w; + a1 += ip[-3] * w; + a2 += ip[-2] * w; + a3 += alpha * mpa[-1]; + } + + ip += Channels; + mp++; + mpa++; + } + + if (aw != 0) + { + float wf = UnscaleToFloat(aw); + wf = 1f / (1f - wf); + a0 = (int)(a0 * wf); + a1 = (int)(a1 * wf); + a2 = (int)(a2 * wf); + } + + tp[0] = UnscaleToInt32(a0); + tp[1] = UnscaleToInt32(a1); + tp[2] = UnscaleToInt32(a2); + tp[3] = UnscaleToInt32(a3); + tp += tstride; + } + } + + void IConvolver8bpc.WriteDestLine(int* tstart, int tstride, byte* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy) + { + byte* op = ostart; + int xc = ox + ow; + + while (ox < xc) + { + int a0 = 0, a1 = 0, a2 = 0, a3 = 0, aw = 0; + + int* tp = tstart + ox * tstride + 2 * Channels; + int* tpe = tp + tstride - 1 * Channels; + int* mp = pmapy + 2, mpa = pmapya + 2; + + while (tp < tpe) + { + int alpha = tp[-5]; + if (alpha == 0) + { + aw += mp[-2]; + } + else + { + int w = mp[-2]; + a0 += tp[-8] * w; + a1 += tp[-7] * w; + a2 += tp[-6] * w; + a3 += alpha * mpa[-2]; + } + + alpha = tp[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += tp[-4] * w; + a1 += tp[-3] * w; + a2 += tp[-2] * w; + a3 += alpha * mpa[-1]; + } + + tp += 2 * Channels; + mp += 2; + mpa+= 2; + } + + tp -= 1 * Channels; + mp -= 1; + mpa-= 1; + while (tp < tpe) + { + int alpha = tp[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += tp[-4] * w; + a1 += tp[-3] * w; + a2 += tp[-2] * w; + a3 += alpha * mpa[-1]; + } + + tp += Channels; + mp++; + mpa++; + } + + if (a3 <= 0) + { + a0 = a1 = a2 = a3 = 0; + } + else if (aw != 0) + { + float wf = UnscaleToFloat(aw); + wf = 1f / (1f - wf); + a0 = (int)(a0 * wf); + a1 = (int)(a1 * wf); + a2 = (int)(a2 * wf); + } + + op[0] = UnscaleToByte(a0); + op[1] = UnscaleToByte(a1); + op[2] = UnscaleToByte(a2); + op[3] = UnscaleToByte(a3); + op += Channels; + ox++; + } + } + + void IConvolver8bpc.SharpenLine(byte* cstart, byte* bstart, byte* ostart, int ox, int ow, int amt, int thresh) + { + int iamt = ScaleToInt32(amt / 100d); + int threshold = thresh; + + byte* ip = cstart, bp = bstart, op = ostart; + int xc = ox + ow; + + for (int x = ox; x < xc; x++, ip += Channels, bp += Channels, op += Channels) + { + byte c0 = ip[0], c1 = ip[1], c2 = ip[2], c3 = ip[3]; + int yi = LumaFromBgr(c0, c1, c2); + int yb = LumaFromBgr(bp[0], bp[1], bp[2]); + if (threshold == 0 || Math.Abs(yi - yb) > threshold) + { + int dif = UnscaleToInt32((yi - yb) * iamt); + op[0] = ClampToByte(c0 + dif); + op[1] = ClampToByte(c1 + dif); + op[2] = ClampToByte(c2 + dif); + op[3] = c3; + } + else + { + op[0] = c0; + op[1] = c1; + op[2] = c2; + op[3] = c3; + } + } + } + } + + unsafe internal class ConvolverBgra16bpc : IConvolver16bpc + { + private const int Channels = 4; + + void IConvolver16bpc.ConvolveSourceLine(ushort* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx) + { + int* pmapx = mapxstart; + int* pmapxa = mapxastart + 1; + int* tp = tstart; + int* tpe = tstart + tlen; + + while (tp < tpe) + { + int a0 = 0, a1 = 0, a2 = 0, a3 = 0, aw = 0; + + int ix = *pmapx++; + ushort* ip = istart + ix * Channels + 4 * Channels; + ushort* ipe = ip + smapx * Channels - 3 * Channels; + int* mp = pmapx + 4, mpa = pmapxa + 4; + pmapx += smapx; + pmapxa += smapx + 1; + + while (ip < ipe) + { + int alpha = ip[-13]; + if (alpha == 0) + { + aw += mp[-4]; + } + else + { + int w = mp[-4]; + a0 += ip[-16] * w; + a1 += ip[-15] * w; + a2 += ip[-14] * w; + a3 += alpha * mpa[-4]; + } + + alpha = ip[-9]; + if (alpha == 0) + { + aw += mp[-3]; + } + else + { + int w = mp[-3]; + a0 += ip[-12] * w; + a1 += ip[-11] * w; + a2 += ip[-10] * w; + a3 += alpha * mpa[-3]; + } + + alpha = ip[-5]; + if (alpha == 0) + { + aw += mp[-2]; + } + else + { + int w = mp[-2]; + a0 += ip[-8] * w; + a1 += ip[-7] * w; + a2 += ip[-6] * w; + a3 += alpha * mpa[-2]; + } + + alpha = ip[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += ip[-4] * w; + a1 += ip[-3] * w; + a2 += ip[-2] * w; + a3 += alpha * mpa[-1]; + } + + ip += 4 * Channels; + mp += 4; + mpa+= 4; + } + + ip -= 3 * Channels; + mp -= 3; + mpa-= 3; + while (ip < ipe) + { + int alpha = ip[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += ip[-4] * w; + a1 += ip[-3] * w; + a2 += ip[-2] * w; + a3 += alpha * mpa[-1]; + } + + ip += Channels; + mp++; + mpa++; + } + + if (aw != 0) + { + float wf = UnscaleToFloat(aw); + wf = 1f / (1f - wf); + a0 = (int)(a0 * wf); + a1 = (int)(a1 * wf); + a2 = (int)(a2 * wf); + } + + tp[0] = UnscaleToInt32(a0); + tp[1] = UnscaleToInt32(a1); + tp[2] = UnscaleToInt32(a2); + tp[3] = UnscaleToInt32(a3); + tp += tstride; + } + } + + void IConvolver16bpc.WriteDestLine(int* tstart, int tstride, ushort* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy) + { + ushort* op = ostart; + int xc = ox + ow; + + while (ox < xc) + { + int a0 = 0, a1 = 0, a2 = 0, a3 = 0, aw = 0; + + int* tp = tstart + ox * tstride + 2 * Channels; + int* tpe = tp + tstride - 1 * Channels; + int* mp = pmapy + 2, mpa = pmapya + 2; + + while (tp < tpe) + { + int alpha = tp[-5]; + if (alpha == 0) + { + aw += mp[-2]; + } + else + { + int w = mp[-2]; + a0 += tp[-8] * w; + a1 += tp[-7] * w; + a2 += tp[-6] * w; + a3 += alpha * mpa[-2]; + } + + alpha = tp[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += tp[-4] * w; + a1 += tp[-3] * w; + a2 += tp[-2] * w; + a3 += alpha * mpa[-1]; + } + + tp += 2 * Channels; + mp += 2; + mpa+= 2; + } + + tp -= 1 * Channels; + mp -= 1; + mpa-= 1; + while (tp < tpe) + { + int alpha = tp[-1]; + if (alpha == 0) + { + aw += mp[-1]; + } + else + { + int w = mp[-1]; + a0 += tp[-4] * w; + a1 += tp[-3] * w; + a2 += tp[-2] * w; + a3 += alpha * mpa[-1]; + } + + tp += Channels; + mp++; + mpa++; + } + + if (a3 <= 0) + { + a0 = a1 = a2 = a3 = 0; + } + else if (aw != 0) + { + float wf = UnscaleToFloat(aw); + wf = 1f / (1f - wf); + a0 = (int)(a0 * wf); + a1 = (int)(a1 * wf); + a2 = (int)(a2 * wf); + } + + op[0] = UnscaleToUInt15(a0); + op[1] = UnscaleToUInt15(a1); + op[2] = UnscaleToUInt15(a2); + op[3] = UnscaleToUInt15(a3); + op += Channels; + ox++; + } + } + + void IConvolver16bpc.SharpenLine(ushort* cstart, ushort* bstart, ushort* ostart, int ox, int ow, int amt, int thresh) + { + fixed (byte* gt = LookupTables.Gamma) + { + int iamt = ScaleToInt32(amt / 100d); + int threshold = thresh; + + ushort* ip = cstart, bp = bstart, op = ostart; + int xc = ox + ow; + + for (int x = ox; x < xc; x++, ip += Channels, bp += Channels, op += Channels) + { + ushort c0 = ip[0], c1 = ip[1], c2 = ip[2], c3 = ip[3]; + int yi = LumaFromBgr(c0, c1, c2); + int yb = LumaFromBgr(bp[0], bp[1], bp[2]); + if (threshold == 0 || Math.Abs(gt[yi] - gt[yb]) > threshold) + { + int dif = UnscaleToInt32((yi - yb) * iamt); + op[0] = ClampToUInt15(c0 + dif); + op[1] = ClampToUInt15(c1 + dif); + op[2] = ClampToUInt15(c2 + dif); + op[3] = c3; + } + else + { + op[0] = c0; + op[1] = c1; + op[2] = c2; + op[3] = c3; + } + } + } + } + } + + unsafe internal class ConvolverBgr8bpc : IConvolver8bpc + { + private const int Channels = 3; + + void IConvolver8bpc.ConvolveSourceLine(byte* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx) + { + int* pmapx = mapxstart; + int* tp = tstart; + int* tpe = tstart + tlen; + + while (tp < tpe) + { + int a0 = 0, a1 = 0, a2 = 0; + + int ix = *pmapx++; + byte* ip = istart + ix * Channels + 5 * Channels; + byte* ipe = ip + smapx * Channels - 4 * Channels; + int* mp = pmapx + 5; + pmapx += smapx; + + while (ip < ipe) + { + int w = mp[-5]; + a0 += ip[-15] * w; + a1 += ip[-14] * w; + a2 += ip[-13] * w; + + w = mp[-4]; + a0 += ip[-12] * w; + a1 += ip[-11] * w; + a2 += ip[-10] * w; + + w = mp[-3]; + a0 += ip[-9] * w; + a1 += ip[-8] * w; + a2 += ip[-7] * w; + + w = mp[-2]; + a0 += ip[-6] * w; + a1 += ip[-5] * w; + a2 += ip[-4] * w; + + w = mp[-1]; + a0 += ip[-3] * w; + a1 += ip[-2] * w; + a2 += ip[-1] * w; + + ip += 5 * Channels; + mp += 5; + } + + ip -= 4 * Channels; + mp -= 4; + while (ip < ipe) + { + int w = mp[-1]; + a0 += ip[-3] * w; + a1 += ip[-2] * w; + a2 += ip[-1] * w; + + ip += Channels; + mp++; + } + + tp[0] = UnscaleToInt32(a0); + tp[1] = UnscaleToInt32(a1); + tp[2] = UnscaleToInt32(a2); + tp += tstride; + } + } + + void IConvolver8bpc.WriteDestLine(int* tstart, int tstride, byte* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy) + { + byte* op = ostart; + int xc = ox + ow; + + while (ox < xc) + { + int a0 = 0, a1 = 0, a2 = 0; + + int* tp = tstart + ox * tstride + 2 * Channels; + int* tpe = tp + tstride - 1 * Channels; + int* mp = pmapy + 2; + + while (tp < tpe) + { + int w = mp[-2]; + a0 += tp[-6] * w; + a1 += tp[-5] * w; + a2 += tp[-4] * w; + + w = mp[-1]; + a0 += tp[-3] * w; + a1 += tp[-2] * w; + a2 += tp[-1] * w; + + tp += 2 * Channels; + mp += 2; + } + + tp -= 1 * Channels; + mp -= 1; + while (tp < tpe) + { + int w = mp[-1]; + a0 += tp[-3] * w; + a1 += tp[-2] * w; + a2 += tp[-1] * w; + + tp += Channels; + mp++; + } + + op[0] = UnscaleToByte(a0); + op[1] = UnscaleToByte(a1); + op[2] = UnscaleToByte(a2); + op += Channels; + ox++; + } + } + + void IConvolver8bpc.SharpenLine(byte* cstart, byte* bstart, byte* ostart, int ox, int ow, int amt, int thresh) + { + int iamt = ScaleToInt32(amt / 100d); + int threshold = thresh; + + byte* ip = cstart, bp = bstart, op = ostart; + int xc = ox + ow; + + for (int x = ox; x < xc; x++, ip += Channels, bp += Channels, op += Channels) + { + byte c0 = ip[0], c1 = ip[1], c2 = ip[2]; + int yi = LumaFromBgr(c0, c1, c2); + int yb = LumaFromBgr(bp[0], bp[1], bp[2]); + if (threshold == 0 || Math.Abs(yi - yb) > threshold) + { + int dif = UnscaleToInt32((yi - yb) * iamt); + op[0] = ClampToByte(c0 + dif); + op[1] = ClampToByte(c1 + dif); + op[2] = ClampToByte(c2 + dif); + } + else + { + op[0] = c0; + op[1] = c1; + op[2] = c2; + } + } + } + } + + unsafe internal class ConvolverBgr16bpc : IConvolver16bpc + { + private const int Channels = 3; + + void IConvolver16bpc.ConvolveSourceLine(ushort* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx) + { + int* pmapx = mapxstart; + int* tp = tstart; + int* tpe = tstart + tlen; + + while (tp < tpe) + { + int a0 = 0, a1 = 0, a2 = 0; + + int ix = *pmapx++; + ushort* ip = istart + ix * Channels + 5 * Channels; + ushort* ipe = ip + smapx * Channels - 4 * Channels; + int* mp = pmapx + 5; + pmapx += smapx; + + while (ip < ipe) + { + int w = mp[-5]; + a0 += ip[-15] * w; + a1 += ip[-14] * w; + a2 += ip[-13] * w; + + w = mp[-4]; + a0 += ip[-12] * w; + a1 += ip[-11] * w; + a2 += ip[-10] * w; + + w = mp[-3]; + a0 += ip[-9] * w; + a1 += ip[-8] * w; + a2 += ip[-7] * w; + + w = mp[-2]; + a0 += ip[-6] * w; + a1 += ip[-5] * w; + a2 += ip[-4] * w; + + w = mp[-1]; + a0 += ip[-3] * w; + a1 += ip[-2] * w; + a2 += ip[-1] * w; + + ip += 5 * Channels; + mp += 5; + } + + ip -= 4 * Channels; + mp -= 4; + while (ip < ipe) + { + int w = mp[-1]; + a0 += ip[-3] * w; + a1 += ip[-2] * w; + a2 += ip[-1] * w; + + ip += Channels; + mp++; + } + + tp[0] = UnscaleToInt32(a0); + tp[1] = UnscaleToInt32(a1); + tp[2] = UnscaleToInt32(a2); + tp += tstride; + } + } + + void IConvolver16bpc.WriteDestLine(int* tstart, int tstride, ushort* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy) + { + ushort* op = ostart; + int xc = ox + ow; + + while (ox < xc) + { + int a0 = 0, a1 = 0, a2 = 0; + + int* tp = tstart + ox * tstride + 2 * Channels; + int* tpe = tp + tstride - 1 * Channels; + int* mp = pmapy + 2; + + while (tp < tpe) + { + int w = mp[-2]; + a0 += tp[-6] * w; + a1 += tp[-5] * w; + a2 += tp[-4] * w; + + w = mp[-1]; + a0 += tp[-3] * w; + a1 += tp[-2] * w; + a2 += tp[-1] * w; + + tp += 2 * Channels; + mp += 2; + } + + tp -= 1 * Channels; + mp -= 1; + while (tp < tpe) + { + int w = mp[-1]; + a0 += tp[-3] * w; + a1 += tp[-2] * w; + a2 += tp[-1] * w; + + tp += Channels; + mp++; + } + + op[0] = UnscaleToUInt15(a0); + op[1] = UnscaleToUInt15(a1); + op[2] = UnscaleToUInt15(a2); + op += Channels; + ox++; + } + } + + void IConvolver16bpc.SharpenLine(ushort* cstart, ushort* bstart, ushort* ostart, int ox, int ow, int amt, int thresh) + { + fixed (byte* gt = LookupTables.Gamma) + { + int iamt = ScaleToInt32(amt / 100d); + int threshold = thresh; + + ushort* ip = cstart, bp = bstart, op = ostart; + int xc = ox + ow; + + for (int x = ox; x < xc; x++, ip += Channels, bp += Channels, op += Channels) + { + ushort c0 = ip[0], c1 = ip[1], c2 = ip[2]; + int yi = LumaFromBgr(c0, c1, c2); + int yb = LumaFromBgr(bp[0], bp[1], bp[2]); + if (threshold == 0 || Math.Abs(gt[yi] - gt[yb]) > threshold) + { + int dif = UnscaleToInt32((yi - yb) * iamt); + op[0] = ClampToUInt15(c0 + dif); + op[1] = ClampToUInt15(c1 + dif); + op[2] = ClampToUInt15(c2 + dif); + } + else + { + op[0] = c0; + op[1] = c1; + op[2] = c2; + } + } + } + } + } + + unsafe internal class ConvolverGrey8bpc : IConvolver8bpc + { + private const int Channels = 1; + + void IConvolver8bpc.ConvolveSourceLine(byte* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx) + { + int* pmapx = mapxstart; + int* tp = tstart; + int* tpe = tstart + tlen; + + while (tp < tpe) + { + int a0 = 0; + + int ix = *pmapx++; + byte* ip = istart + ix * Channels + 8 * Channels; + byte* ipe = ip + smapx * Channels - 7 * Channels; + int* mp = pmapx + 8; + pmapx += smapx; + + while (ip < ipe) + { + a0 += ip[-8] * mp[-8]; + a0 += ip[-7] * mp[-7]; + a0 += ip[-6] * mp[-6]; + a0 += ip[-5] * mp[-5]; + a0 += ip[-4] * mp[-4]; + a0 += ip[-3] * mp[-3]; + a0 += ip[-2] * mp[-2]; + a0 += ip[-1] * mp[-1]; + ip += 8 * Channels; + mp += 8; + } + + ip -= 7 * Channels; + mp -= 7; + while (ip < ipe) + { + a0 += ip[-1] * mp[-1]; + + ip += Channels; + mp++; + } + + tp[0] = UnscaleToInt32(a0); + tp += tstride; + } + } + + void IConvolver8bpc.WriteDestLine(int* tstart, int tstride, byte* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy) + { + byte* op = ostart; + int xc = ox + ow; + + while (ox < xc) + { + int a0 = 0; + + int* tp = tstart + ox * tstride + 4 * Channels; + int* tpe = tp + tstride - 3 * Channels; + int* mp = pmapy + 4; + + while (tp < tpe) + { + a0 += tp[-4] * mp[-4]; + a0 += tp[-3] * mp[-3]; + a0 += tp[-2] * mp[-2]; + a0 += tp[-1] * mp[-1]; + tp += 4 * Channels; + mp += 4; + } + + tp -= 3 * Channels; + mp -= 3; + while (tp < tpe) + { + a0 += tp[-1] * mp[-1]; + + tp += Channels; + mp++; + } + + op[0] = UnscaleToByte(a0); + op += Channels; + ox++; + } + } + + void IConvolver8bpc.SharpenLine(byte* cstart, byte* bstart, byte* ostart, int ox, int ow, int amt, int thresh) + { + int iamt = ScaleToInt32(amt / 100d); + int threshold = thresh; + + byte* ip = cstart, bp = bstart, op = ostart; + int xc = ox + ow; + + for (int x = ox; x < xc; x++, ip += Channels, bp += Channels, op += Channels) + { + byte c0 = ip[0]; + int yi = c0; + int yb = bp[0]; + if (threshold == 0 || Math.Abs(yi - yb) > threshold) + { + int dif = UnscaleToInt32((yi - yb) * iamt); + op[0] = ClampToByte(c0 + dif); + } + else + { + op[0] = c0; + } + } + } + } + + unsafe internal class ConvolverGrey16bpc : IConvolver16bpc + { + private const int Channels = 1; + + void IConvolver16bpc.ConvolveSourceLine(ushort* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx) + { + int* pmapx = mapxstart; + int* tp = tstart; + int* tpe = tstart + tlen; + + while (tp < tpe) + { + int a0 = 0; + + int ix = *pmapx++; + ushort* ip = istart + ix * Channels + 8 * Channels; + ushort* ipe = ip + smapx * Channels - 7 * Channels; + int* mp = pmapx + 8; + pmapx += smapx; + + while (ip < ipe) + { + a0 += ip[-8] * mp[-8]; + a0 += ip[-7] * mp[-7]; + a0 += ip[-6] * mp[-6]; + a0 += ip[-5] * mp[-5]; + a0 += ip[-4] * mp[-4]; + a0 += ip[-3] * mp[-3]; + a0 += ip[-2] * mp[-2]; + a0 += ip[-1] * mp[-1]; + ip += 8 * Channels; + mp += 8; + } + + ip -= 7 * Channels; + mp -= 7; + while (ip < ipe) + { + a0 += ip[-1] * mp[-1]; + + ip += Channels; + mp++; + } + + tp[0] = UnscaleToInt32(a0); + tp += tstride; + } + } + + void IConvolver16bpc.WriteDestLine(int* tstart, int tstride, ushort* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy) + { + ushort* op = ostart; + int xc = ox + ow; + + while (ox < xc) + { + int a0 = 0; + + int* tp = tstart + ox * tstride + 4 * Channels; + int* tpe = tp + tstride - 3 * Channels; + int* mp = pmapy + 4; + + while (tp < tpe) + { + a0 += tp[-4] * mp[-4]; + a0 += tp[-3] * mp[-3]; + a0 += tp[-2] * mp[-2]; + a0 += tp[-1] * mp[-1]; + tp += 4 * Channels; + mp += 4; + } + + tp -= 3 * Channels; + mp -= 3; + while (tp < tpe) + { + a0 += tp[-1] * mp[-1]; + + tp += Channels; + mp++; + } + + op[0] = UnscaleToUInt15(a0); + op += Channels; + ox++; + } + } + + void IConvolver16bpc.SharpenLine(ushort* cstart, ushort* bstart, ushort* ostart, int ox, int ow, int amt, int thresh) + { + fixed (byte* gt = LookupTables.Gamma) + { + int iamt = ScaleToInt32(amt / 100d); + int threshold = thresh; + + ushort* ip = cstart, bp = bstart, op = ostart; + int xc = ox + ow; + + for (int x = ox; x < xc; x++, ip += Channels, bp += Channels, op += Channels) + { + ushort c0 = ip[0]; + int yi = c0; + int yb = bp[0]; + if (threshold == 0 || Math.Abs(gt[yi] - gt[yb]) > threshold) + { + int dif = UnscaleToInt32((yi - yb) * iamt); + op[0] = ClampToUInt15(c0 + dif); + } + else + { + op[0] = c0; + } + } + } + } + } + +} diff --git a/src/WIC/Generated/WicConvolution.cs b/src/WIC/Generated/WicConvolution.cs new file mode 100644 index 00000000..fdfc4754 --- /dev/null +++ b/src/WIC/Generated/WicConvolution.cs @@ -0,0 +1,314 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + unsafe internal interface IConvolver8bpc + { + void ConvolveSourceLine(byte* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx); + void WriteDestLine(int* tstart, int tstride, byte* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy); + void SharpenLine(byte* cstart, byte* bstart, byte* ostart, int ox, int ow, int amt, int thresh); + } + + unsafe internal interface IConvolver16bpc + { + void ConvolveSourceLine(ushort* istart, int* tstart, int tstride, int tlen, int* mapxstart, int* mapxastart, int smapx); + void WriteDestLine(int* tstart, int tstride, ushort* ostart, int ox, int ow, int* pmapy, int* pmapya, int smapy); + void SharpenLine(ushort* cstart, ushort* bstart, ushort* ostart, int ox, int ow, int amt, int thresh); + } + + internal class WicConvolution8bpc : WicBitmapSourceBase + { + protected bool BufferSource; + protected byte[] LineBuff; + protected int[] IntBuff; + protected int IntStride; + protected int IntStartLine; + protected uint OutWidth; + protected uint OutHeight; + protected KernelMap XMap; + protected KernelMap YMap; + protected KernelMap XMapAlpha; + protected KernelMap YMapAlpha; + protected WICRect SourceRect; + protected IConvolver8bpc Processor; + + public WicConvolution8bpc(IWICBitmapSource source, KernelMap mapx, KernelMap mapy, bool bufferSource = false) : base(source) + { + BufferSource = bufferSource; + XMap = mapx; + YMap = mapy; + XMapAlpha = mapx.MakeAlphaMap(); + YMapAlpha = mapy.MakeAlphaMap(); + OutWidth = (uint)mapx.OutPixelCount; + OutHeight = (uint)mapy.OutPixelCount; + SourceRect = new WICRect() { Width = (int)Width, Height = 1 }; + + if (Format == Consts.GUID_WICPixelFormat32bppPBGRA) + Processor = new ConvolverBgra8bpc(); + else if (Format == Consts.GUID_WICPixelFormat32bppBGRA) + Processor = new ConvolverBgra8bpc(); + else if (Format == Consts.GUID_WICPixelFormat24bppBGR) + Processor = new ConvolverBgr8bpc(); + else if (Format == Consts.GUID_WICPixelFormat8bppGray) + Processor = new ConvolverGrey8bpc(); + else if (Format == Consts.GUID_WICPixelFormat8bppY) + Processor = new ConvolverGrey8bpc(); + else + throw new NotSupportedException("Unsupported pixel format"); + + Stride /= 1; + IntStride = (int)(mapy.SampleCount * Channels); + + LineBuff = new byte[(bufferSource ? mapy.SampleCount : 1) * Stride]; + IntBuff = new int[OutWidth * IntStride]; + IntStartLine = -mapy.SampleCount; + } + + public override void GetSize(out uint puiWidth, out uint puiHeight) + { + puiWidth = OutWidth; + puiHeight = OutHeight; + } + + unsafe public override void CopyPixels(WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer) + { + fixed (byte* bstart = LineBuff) + fixed (int* mapystart = YMap.Map, mapxstart = XMap.Map, mapxastart = XMapAlpha.Map, mapyastart = YMapAlpha.Map) + fixed (int* tstart = IntBuff) + { + int oh = prc.Height, ow = prc.Width, ox = prc.X, oy = prc.Y; + int smapy = YMap.SampleCount; + + for (int y = 0; y < oh; y++) + { + int* pmapy = mapystart + ((oy + y) * (smapy + 1)); + int* pmapya = mapyastart + ((oy + y) * (smapy + 1)) + 1; + int iy = *pmapy++; + LoadBuffer(bstart, tstart, mapxstart, mapxastart, iy); + + void* op = (byte*)pbBuffer + y * cbStride; + ConvolveLine(bstart, tstart, (byte*)op, pmapy, pmapya, smapy, ox, oy + y, ow); + } + } + } + + unsafe protected virtual void ConvolveLine(byte* bstart, int* tstart, byte* ostart, int* pmapy, int* pmapya, int smapy, int ox, int oy, int ow) + { + Processor.WriteDestLine(tstart, IntStride, ostart, ox, ow, pmapy, pmapya, smapy); + } + + unsafe protected void LoadBuffer(byte* bstart, int* tstart, int* mapxstart, int* mapxastart, int iy) + { + int smapy = YMap.SampleCount; + + if (iy < IntStartLine) + IntStartLine = iy - YMap.SampleCount; + + int tc = Math.Min(iy - IntStartLine, smapy); + if (tc > 0) + { + IntStartLine = iy; + + int tk = smapy - tc; + if (tk > 0) + { + if (BufferSource) + Buffer.MemoryCopy(bstart + tc * Stride, bstart, LineBuff.LongLength * 1, tk * Stride * 1); + + Buffer.MemoryCopy(tstart + tc * Channels, tstart, IntBuff.LongLength * 4, (IntBuff.LongLength - tc * Channels) * 4); + } + + for (int ty = tk; ty < smapy; ty++) + { + byte* bline = BufferSource ? bstart + ty * Stride : bstart; + int* tline = tstart + ty * Channels; + + SourceRect.Y = iy + ty; + Source.CopyPixels(SourceRect, Stride * 1, Stride * 1, (IntPtr)bline); + + Processor.ConvolveSourceLine(bline, tline, IntStride, IntBuff.Length, mapxstart, mapxastart, XMap.SampleCount); + } + } + } + } + + internal class WicUnsharpMask8bpc : WicConvolution8bpc + { + private UnsharpMaskSettings sharpenSettings; + private byte[] blurBuff; + + public WicUnsharpMask8bpc(IWICBitmapSource source, KernelMap mapx, KernelMap mapy, UnsharpMaskSettings ss) : base(source, mapx, mapy, true) + { + sharpenSettings = ss; + blurBuff = new byte[Stride]; + } + + unsafe protected override void ConvolveLine(byte* bstart, int* tstart, byte* ostart, int* pmapy, int* pmapya, int smapy, int ox, int oy, int ow) + { + fixed (byte* blurstart = blurBuff) + { + Processor.WriteDestLine(tstart, IntStride, blurstart, ox, ow, pmapy, pmapya, smapy); + + int by = (int)Height - 1 - oy; + int cy = smapy / 2; + if (cy > oy) + cy = oy; + else if (cy > by) + cy += cy - by; + + Processor.SharpenLine(bstart + cy * Stride, blurstart, ostart, ox, ow, sharpenSettings.Amount, sharpenSettings.Threshold); + } + } + } + + internal class WicConvolution16bpc : WicBitmapSourceBase + { + protected bool BufferSource; + protected ushort[] LineBuff; + protected int[] IntBuff; + protected int IntStride; + protected int IntStartLine; + protected uint OutWidth; + protected uint OutHeight; + protected KernelMap XMap; + protected KernelMap YMap; + protected KernelMap XMapAlpha; + protected KernelMap YMapAlpha; + protected WICRect SourceRect; + protected IConvolver16bpc Processor; + + public WicConvolution16bpc(IWICBitmapSource source, KernelMap mapx, KernelMap mapy, bool bufferSource = false) : base(source) + { + BufferSource = bufferSource; + XMap = mapx; + YMap = mapy; + XMapAlpha = mapx.MakeAlphaMap(); + YMapAlpha = mapy.MakeAlphaMap(); + OutWidth = (uint)mapx.OutPixelCount; + OutHeight = (uint)mapy.OutPixelCount; + SourceRect = new WICRect() { Width = (int)Width, Height = 1 }; + + if (Format == Consts.GUID_WICPixelFormat64bppBGRA) + Processor = new ConvolverBgra16bpc(); + else if (Format == Consts.GUID_WICPixelFormat48bppBGR) + Processor = new ConvolverBgr16bpc(); + else if (Format == Consts.GUID_WICPixelFormat16bppGray) + Processor = new ConvolverGrey16bpc(); + else + throw new NotSupportedException("Unsupported pixel format"); + + Stride /= 2; + IntStride = (int)(mapy.SampleCount * Channels); + + LineBuff = new ushort[(bufferSource ? mapy.SampleCount : 1) * Stride]; + IntBuff = new int[OutWidth * IntStride]; + IntStartLine = -mapy.SampleCount; + } + + public override void GetSize(out uint puiWidth, out uint puiHeight) + { + puiWidth = OutWidth; + puiHeight = OutHeight; + } + + unsafe public override void CopyPixels(WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer) + { + fixed (ushort* bstart = LineBuff) + fixed (int* mapystart = YMap.Map, mapxstart = XMap.Map, mapxastart = XMapAlpha.Map, mapyastart = YMapAlpha.Map) + fixed (int* tstart = IntBuff) + { + int oh = prc.Height, ow = prc.Width, ox = prc.X, oy = prc.Y; + int smapy = YMap.SampleCount; + + for (int y = 0; y < oh; y++) + { + int* pmapy = mapystart + ((oy + y) * (smapy + 1)); + int* pmapya = mapyastart + ((oy + y) * (smapy + 1)) + 1; + int iy = *pmapy++; + LoadBuffer(bstart, tstart, mapxstart, mapxastart, iy); + + void* op = (byte*)pbBuffer + y * cbStride; + ConvolveLine(bstart, tstart, (ushort*)op, pmapy, pmapya, smapy, ox, oy + y, ow); + } + } + } + + unsafe protected virtual void ConvolveLine(ushort* bstart, int* tstart, ushort* ostart, int* pmapy, int* pmapya, int smapy, int ox, int oy, int ow) + { + Processor.WriteDestLine(tstart, IntStride, ostart, ox, ow, pmapy, pmapya, smapy); + } + + unsafe protected void LoadBuffer(ushort* bstart, int* tstart, int* mapxstart, int* mapxastart, int iy) + { + int smapy = YMap.SampleCount; + + if (iy < IntStartLine) + IntStartLine = iy - YMap.SampleCount; + + int tc = Math.Min(iy - IntStartLine, smapy); + if (tc > 0) + { + IntStartLine = iy; + + int tk = smapy - tc; + if (tk > 0) + { + if (BufferSource) + Buffer.MemoryCopy(bstart + tc * Stride, bstart, LineBuff.LongLength * 2, tk * Stride * 2); + + Buffer.MemoryCopy(tstart + tc * Channels, tstart, IntBuff.LongLength * 4, (IntBuff.LongLength - tc * Channels) * 4); + } + + for (int ty = tk; ty < smapy; ty++) + { + ushort* bline = BufferSource ? bstart + ty * Stride : bstart; + int* tline = tstart + ty * Channels; + + SourceRect.Y = iy + ty; + Source.CopyPixels(SourceRect, Stride * 2, Stride * 2, (IntPtr)bline); + + Processor.ConvolveSourceLine(bline, tline, IntStride, IntBuff.Length, mapxstart, mapxastart, XMap.SampleCount); + } + } + } + } + + internal class WicUnsharpMask16bpc : WicConvolution16bpc + { + private UnsharpMaskSettings sharpenSettings; + private ushort[] blurBuff; + + public WicUnsharpMask16bpc(IWICBitmapSource source, KernelMap mapx, KernelMap mapy, UnsharpMaskSettings ss) : base(source, mapx, mapy, true) + { + sharpenSettings = ss; + blurBuff = new ushort[Stride]; + } + + unsafe protected override void ConvolveLine(ushort* bstart, int* tstart, ushort* ostart, int* pmapy, int* pmapya, int smapy, int ox, int oy, int ow) + { + fixed (ushort* blurstart = blurBuff) + { + Processor.WriteDestLine(tstart, IntStride, blurstart, ox, ow, pmapy, pmapya, smapy); + + int by = (int)Height - 1 - oy; + int cy = smapy / 2; + if (cy > oy) + cy = oy; + else if (cy > by) + cy += cy - by; + + Processor.SharpenLine(bstart + cy * Stride, blurstart, ostart, ox, ow, sharpenSettings.Amount, sharpenSettings.Threshold); + } + } + } + +} diff --git a/src/WIC/KernelMap.cs b/src/WIC/KernelMap.cs new file mode 100644 index 00000000..aa9e895e --- /dev/null +++ b/src/WIC/KernelMap.cs @@ -0,0 +1,188 @@ +using System; +using System.Linq; + +namespace PhotoSauce.MagicScaler +{ + internal class KernelMap + { + public int InPixelCount { get; private set; } + public int OutPixelCount { get; private set; } + public int SampleCount { get; private set; } + public int[] Map { get; private set; } + + public KernelMap(int inPixels, int outPixels, int samples) + { + InPixelCount = inPixels; + OutPixelCount = outPixels; + SampleCount = samples; + Map = new int[outPixels * (samples + 1)]; + } + + unsafe private KernelMap clamp() + { + fixed (int* mstart = Map) + { + int* mp = mstart; + int* mpe = mstart + Map.Length; + int samp = SampleCount, ipix = InPixelCount; + while (mp < mpe) + { + int start = *mp; + if (start < 0) + { + *mp = 0; + int* mpw = mp + 1; + int o = 0 - start; + int a = 0; + for (int j = 0; j <= o; j++) + a += mpw[j]; + + mpw[0] = a; + for (int j = 1; j < samp; j++) + mpw[j] = j < samp - o ? mpw[j + o] : 0; + } + else if (start + samp > ipix) + { + int ns = ipix - samp; + int last = samp - 1; + + *mp = ns; + int* mpw = mp + 1; + int o = start - ns; + int a = 0; + for (int j = 0; j <= o; j++) + a += mpw[last - j]; + + mpw[last] = a; + for (int j = last - 1; j >= 0; j--) + mpw[j] = j >= o ? mpw[j - o] : 0; + } + + mp += samp + 1; + } + } + + return this; + } + + unsafe public KernelMap MakeAlphaMap() + { + if (InPixelCount == OutPixelCount) + return this; + + var interpolator = InterpolationSettings.Hermite.WeightingFunction; + double blur = 0.9; + + double wfactor = Math.Min((double)OutPixelCount / InPixelCount, 1d); + double wdist = Math.Min(interpolator.Support / wfactor * blur, InPixelCount / 2d); + int wsize = (int)Math.Ceiling(wdist * 2d); + + if (wsize >= SampleCount) + return this; + + wsize = SampleCount; + int osize = OutPixelCount; + var map = new KernelMap(InPixelCount, OutPixelCount, SampleCount); + + fixed (int* istart = Map, ostart = map.Map) + { + double* wp = stackalloc double[wsize]; + int* ip = istart, op = ostart; + + double inc = (double)InPixelCount / OutPixelCount; + double midpoint = ((double)InPixelCount - OutPixelCount) / (OutPixelCount * 2d); + + for (int i = 0; i < osize; i++) + { + int start = *ip++; + *op++ = start; + + double weightsum = 0d; + for (int j = 0; j < wsize; j++) + { + double weight = interpolator.GetValue(Math.Abs(((double)start + j - midpoint) * wfactor / blur)); + weightsum += weight; + wp[j] = weight; + } + + for (int j = 0; j < wsize; j++, ip++) + *op++ = MathUtil.ScaleToInt32(wp[j] / weightsum); + + midpoint += inc; + } + } + + return map.clamp(); + } + + unsafe public static KernelMap MakeScaleMap(uint isize, uint osize, InterpolationSettings interpolator) + { + double offs = interpolator.WeightingFunction.Support <= 0.1 ? 0.5 : 0.0; + double blur = interpolator.Blur; + if (interpolator.WeightingFunction.Support <= 0.5) + blur = 1d; + + double wfactor = Math.Min((double)osize / isize, 1d); + double wdist = Math.Min(interpolator.WeightingFunction.Support / wfactor * blur, isize / 2d); + int wsize = (int)Math.Ceiling(wdist * 2d); + + var map = new KernelMap((int)isize, (int)osize, wsize); + fixed (int* mapstart = map.Map) + { + double* wp = stackalloc double[wsize]; + int* mp = mapstart; + + double inc = (double)isize / osize; + double midpoint = ((double)isize - osize) / (osize * 2d) + offs; + + for (int i = 0; i < osize; i++) + { + int end = (int)(midpoint + wdist); + int start = end - wsize + 1; + + *mp++ = start; + + double weightsum = 0d; + for (int j = 0; j < wsize; j++) + { + double weight = interpolator.WeightingFunction.GetValue(Math.Abs(((double)start + j - midpoint) * wfactor / blur)); + weightsum += weight; + wp[j] = weight; + } + + for (int j = 0; j < wsize; j++) + *mp++ = MathUtil.ScaleToInt32(wp[j] / weightsum); + + midpoint += inc; + } + } + + return map.clamp(); + } + + unsafe public static KernelMap MakeBlurMap(uint size, double radius) + { + int[] blurkernel = new MathUtil.GaussianFactory(radius).MakeKernel().Select(f => MathUtil.ScaleToInt32(f)).ToArray(); + int blurdist = blurkernel.Length; + + var map = new KernelMap((int)size, (int)size, blurdist); + fixed (int* mstart = map.Map, kstart = blurkernel) + { + int* mp = mstart, kp = kstart; + + for (int i = 0; i < size; i++) + { + int end = i + blurdist / 2; + int start = end - blurdist + 1; + + *mp++ = start; + + for (int j = 0; j < blurdist; j++) + *mp++ = kp[j]; + } + } + + return map.clamp(); + } + } +} \ No newline at end of file diff --git a/src/WIC/MagicImageProcessor.cs b/src/WIC/MagicImageProcessor.cs new file mode 100644 index 00000000..74879874 --- /dev/null +++ b/src/WIC/MagicImageProcessor.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; +using System.Diagnostics.Contracts; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + public static class MagicImageProcessor + { + public static void ProcessImage(string imgPath, Stream ostm, ProcessImageSettings s) + { + Contract.Requires(imgPath != null, nameof(imgPath)); + Contract.Requires(ostm != null, nameof(ostm)); + Contract.Requires(ostm.CanSeek && ostm.CanWrite, "Output Stream must allow Seek and Write"); + + using (var ctx = new WicProcessingContext(s)) + using (var dec = new WicDecoder(imgPath, ctx)) + processImage(dec, ctx, ostm); + } + + public static void ProcessImage(byte[] imgBuffer, Stream ostm, ProcessImageSettings s) + { + Contract.Requires(imgBuffer != null, nameof(imgBuffer)); + Contract.Requires(ostm != null, nameof(ostm)); + Contract.Requires(ostm.CanSeek && ostm.CanWrite, "Output Stream must allow Seek and Write"); + + using (var ctx = new WicProcessingContext(s)) + using (var dec = new WicDecoder(imgBuffer, ctx)) + processImage(dec, ctx, ostm); + } + + public static void ProcessImage(Stream istm, Stream ostm, ProcessImageSettings s) + { + Contract.Requires(istm != null, nameof(istm)); + Contract.Requires(ostm != null, nameof(ostm)); + Contract.Requires(istm.CanSeek && istm.CanRead, "Input Stream must allow Seek and Read"); + Contract.Requires(ostm.CanSeek && ostm.CanWrite, "Output Stream must allow Seek and Write"); + Contract.Assume(istm.Position < istm.Length, "Input Stream Position is at the end. Did you forget to Seek?"); + + using (var ctx = new WicProcessingContext(s)) + using (var dec = new WicDecoder(istm, ctx)) + processImage(dec, ctx, ostm); + } + + private static void processImage(WicDecoder dec, WicProcessingContext ctx, Stream ostm) + { + using (var frm = new WicFrameReader(dec, ctx)) + using (var met = new WicMetadataReader(frm)) + { + if (!ctx.Settings.Normalized) + ctx.Settings.Fixup((int)ctx.Width, (int)ctx.Height, ctx.IsRotated90); + + bool mod1 = (!ctx.IsSubsampled || ctx.Settings.HybridScaleRatio > 1d || (ctx.Settings.Crop.Width % 2 == 0 && ctx.Settings.Crop.Height % 2 == 0) || (ctx.Settings.Crop.Width == ctx.Width && ctx.Settings.Crop.Height == ctx.Height)); + bool planar = ctx.SupportsPlanar && mod1; + if (planar && ctx.Settings.HybridMode != HybridScaleMode.Off) + { + MagicPlanarImageProcessor.ProcessImage(met, ctx, ostm); + return; + } + + using (var qsc = new WicNativeScaler(met)) + using (var rot = new WicExifRotator(qsc)) + using (var cac = new WicConditionalCache(rot)) + using (var crp = new WicCropper(cac)) + using (var pix = new WicPixelFormatConverter(crp)) + using (var res = new WicScaler(pix, true)) + using (var cmy = new WicCmykConverter(res)) + using (var lll = new WicGammaExpand(cmy)) + using (var mmm = new WicHighQualityScaler(lll)) + using (var mat = new WicMatteTransform(mmm)) + using (var ggg = new WicGammaCompress(mat)) + using (var csc = new WicColorspaceConverter(ggg)) + using (var sss = new WicUnsharpMask(csc)) + using (var dit = new WicPaletizer(sss, 256)) + using (var enc = new WicEncoder(ostm.AsIStream(), ctx)) + enc.WriteSource(dit); + } + } + } +} diff --git a/src/WIC/MagicPlanarImageProcessor.cs b/src/WIC/MagicPlanarImageProcessor.cs new file mode 100644 index 00000000..c14bc9f9 --- /dev/null +++ b/src/WIC/MagicPlanarImageProcessor.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal static class MagicPlanarImageProcessor + { + public static Stream ProcessImage(WicTransform prev, WicProcessingContext ctx, Stream ostm) + { + bool savePlanar = ctx.Settings.SaveFormat == FileFormat.Jpeg && ctx.SourceColorContext == null; + + using (var rot = new WicExifRotator(prev)) + using (var pln = new WicPlanarCache(rot)) + using (var ply = new WicPlanarSplitter(pln, WicPlane.Luma)) + using (var lll = new WicGammaExpand(ply)) + using (var mmm = new WicHighQualityScaler(lll)) + using (var ggg = new WicGammaCompress(mmm)) + using (var sss = new WicUnsharpMask(ggg)) + + using (var enc = new WicEncoder(ostm.AsIStream(), ctx)) + using (var plc = new WicPlanarSplitter(pln, WicPlane.Chroma)) + { + if (savePlanar) + { + var subsample = ctx.Settings.JpegSubsampleMode; + if (subsample == ChromaSubsampleMode.Subsample420) + ctx.Settings.Height = (int)Math.Ceiling(ctx.Settings.Height / 2d); + + if (subsample == ChromaSubsampleMode.Subsample420 || subsample == ChromaSubsampleMode.Subsample422) + ctx.Settings.Width = (int)Math.Ceiling(ctx.Settings.Width / 2d); + + using (var res = new WicScaler(plc)) + using (var pen = new WicPlanarEncoder(enc)) + pen.WriteSource(sss, res); + } + else + { + using (var res = new WicScaler(plc)) + using (var con = new WicPlanarConverter(sss, res)) + using (var pal = new WicPaletizer(con, 256)) + enc.WriteSource(pal); + } + } + + return ostm; + } + } +} \ No newline at end of file diff --git a/src/WIC/WicBase.cs b/src/WIC/WicBase.cs new file mode 100644 index 00000000..ed860908 --- /dev/null +++ b/src/WIC/WicBase.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal abstract class WicBase : IDisposable + { + private bool disposed = false; + protected static readonly IWICImagingFactory Wic = new WICImagingFactory2() as IWICImagingFactory; + + private readonly Stack comHandles = new Stack(); + + protected T AddRef(T comHandle) where T : class + { + Contract.Assert(Marshal.IsComObject(comHandle), "Not a COM object"); + + comHandles.Push(comHandle); + + return comHandle; + } + + private void PopRelease() + { + var h = comHandles.Pop(); + if (h != null && Marshal.IsComObject(h)) + Marshal.ReleaseComObject(h); + } + + protected void Release(T comHandle) where T : class + { + Contract.Assert(ReferenceEquals(comHandles.Peek(), comHandle), "Release() should only be called on the last handle passed to AddRef()"); + + PopRelease(); + } + + protected T AddOwnRef(T comHandle) where T : class + { + if (comHandle == null) + return null; + + var punk = Marshal.GetIUnknownForObject(comHandle); + var newHandle = (T)Marshal.GetUniqueObjectForIUnknown(punk); + Marshal.Release(punk); + + return AddRef(newHandle); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + while (comHandles.Count > 0) + PopRelease(); + + disposed = true; + } + + public void Dispose() + { + Dispose(true); + } + } + + internal class WicProcessingContext : WicBase + { + private IWICColorContext sourceColorContext; + private IWICColorContext destColorContext; + private IWICPalette destPalette; + + public ProcessImageSettings Settings; + public Guid ContainerFormat; + public uint ContainerFrameCount; + public Guid PixelFormat; + public uint Width; + public uint Height; + public WICBitmapTransformOptions TransformOptions; + public IDictionary Metadata; + public uint[] CustomPalette; + public bool SupportsPlanar; + public bool IsSubsampled; + public bool HasAlpha; + public bool IsGreyscale; + public bool IsCmyk; + public bool NeedsCache; + + public bool IsRotated90 => TransformOptions.HasFlag(WICBitmapTransformOptions.WICBitmapTransformRotate90); + + public IWICColorContext SourceColorContext { get { return sourceColorContext; } set { sourceColorContext = AddOwnRef(value); } } + public IWICColorContext DestColorContext { get { return destColorContext; } set { destColorContext = AddOwnRef(value); } } + public IWICPalette DestPalette { get { return destPalette; } set { destPalette = AddOwnRef(value); } } + + public WicProcessingContext(ProcessImageSettings settings) + { + Settings = settings.Clone(); + } + } +} diff --git a/src/WIC/WicBitmapSource.cs b/src/WIC/WicBitmapSource.cs new file mode 100644 index 00000000..ab1c7212 --- /dev/null +++ b/src/WIC/WicBitmapSource.cs @@ -0,0 +1,57 @@ +using System; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal abstract class WicBitmapSourceBase : WicBase, IWICBitmapSource + { + protected IWICBitmapSource Source; + protected Guid Format; + protected uint Width; + protected uint Height; + protected uint Channels; + protected uint Bpp; + protected uint Stride; + + protected WicBitmapSourceBase() { } + + protected WicBitmapSourceBase(IWICBitmapSource source) + { + Source = source; + + Source.GetSize(out Width, out Height); + Format = Source.GetPixelFormat(); + + var pfi = AddRef(Wic.CreateComponentInfo(Format)) as IWICPixelFormatInfo; + Channels = pfi.GetChannelCount(); + Bpp = pfi.GetBitsPerPixel() / 8u; + Release(pfi); + + Stride = Width * Bpp + 3u & ~3u; + } + + public virtual void GetSize(out uint puiWidth, out uint puiHeight) + { + puiWidth = Width; + puiHeight = Height; + } + + public virtual Guid GetPixelFormat() + { + return Format; + } + + public virtual void GetResolution(out double pDpiX, out double pDpiY) + { + Source.GetResolution(out pDpiX, out pDpiY); + } + + public virtual void CopyPalette(IWICPalette pIPalette) + { + Source.CopyPalette(pIPalette); + } + + public abstract void CopyPixels(WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer); + } +} diff --git a/src/WIC/WicCodec.cs b/src/WIC/WicCodec.cs new file mode 100644 index 00000000..bb092fec --- /dev/null +++ b/src/WIC/WicCodec.cs @@ -0,0 +1,185 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal class WicDecoder : WicBase + { + public IWICBitmapDecoder Decoder { get; private set; } + + private void init(IWICBitmapDecoder dec, WicProcessingContext ctx) + { + Decoder = AddRef(dec); + + ctx.ContainerFormat = dec.GetContainerFormat(); + ctx.ContainerFrameCount = dec.GetFrameCount(); + } + + private IWICBitmapDecoder checkDecoder(Func factory) + { + try + { + return factory(); + } + catch (COMException ex) when ((uint)ex.HResult == 0x88982F50) + { + throw new FileFormatException("Image format not supported. Please ensure the input file is an image and that a WIC codec capable of reading the image is installed.", ex); + } + } + + public WicDecoder(string fileName, WicProcessingContext ctx) + { + init(checkDecoder(() => Wic.CreateDecoderFromFilename(fileName, null, GenericAccessRights.GENERIC_READ, WICDecodeOptions.WICDecodeMetadataCacheOnDemand)), ctx); + } + + public WicDecoder(Stream inFile, WicProcessingContext ctx) + { + var stm = AddRef(Wic.CreateStream()); + stm.InitializeFromIStream(inFile.AsIStream()); + init(checkDecoder(() => Wic.CreateDecoderFromStream(stm, null, WICDecodeOptions.WICDecodeMetadataCacheOnDemand)), ctx); + } + + public WicDecoder(byte[] inBuffer, WicProcessingContext ctx) + { + var stm = AddRef(Wic.CreateStream()); + stm.InitializeFromMemory(inBuffer, (uint)inBuffer.Length); + init(checkDecoder(() => Wic.CreateDecoderFromStream(stm, null, WICDecodeOptions.WICDecodeMetadataCacheOnDemand)), ctx); + } + } + + internal class WicEncoder : WicBase + { + public IWICBitmapEncoder Encoder { get; private set; } + public IWICBitmapFrameEncode Frame { get; private set; } + + public WicEncoder(IStream stm, WicProcessingContext ctx) + { + var frame = (IWICBitmapFrameEncode)null; + if (ctx.Settings.SaveFormat == FileFormat.Jpeg) + { + Encoder = AddRef(Wic.CreateEncoder(Consts.GUID_ContainerFormatJpeg, null)); + Encoder.Initialize(stm, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); + + var bag = (IPropertyBag2)null; + Encoder.CreateNewFrame(out frame, ref bag); + AddRef(frame); + AddRef(bag); + + if (ctx.Settings.JpegSubsampleMode != ChromaSubsampleMode.Default) + { + var props = new PROPBAG2[] { new PROPBAG2 { pstrName = "ImageQuality" }, new PROPBAG2 { pstrName = "JpegYCrCbSubsampling" } }; + bag.Write(2, props, new object[] { ctx.Settings.JpegQuality / 100f, (byte)ctx.Settings.JpegSubsampleMode }); + } + else + { + var props = new PROPBAG2[] { new PROPBAG2 { pstrName = "ImageQuality" } }; + bag.Write(1, props, new object[] { ctx.Settings.JpegQuality / 100f }); + } + + frame.Initialize(bag); + } + else if (ctx.Settings.SaveFormat == FileFormat.Gif) + { + Encoder = AddRef(Wic.CreateEncoder(Consts.GUID_ContainerFormatGif, null)); + Encoder.Initialize(stm, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); + Encoder.SetPalette(ctx.DestPalette); + + Encoder.CreateNewFrame(out frame, null); + AddRef(frame); + + frame.Initialize(null); + } + else if (ctx.Settings.SaveFormat == FileFormat.Bmp) + { + Encoder = AddRef(Wic.CreateEncoder(Consts.GUID_ContainerFormatBmp, null)); + Encoder.Initialize(stm, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); + + var bag = (IPropertyBag2)null; + Encoder.CreateNewFrame(out frame, ref bag); + AddRef(frame); + AddRef(bag); + + var props = new PROPBAG2[] { new PROPBAG2 { pstrName = "EnableV5Header32bppBGRA" } }; + bag.Write(1, props, new object[] { ctx.PixelFormat == Consts.GUID_WICPixelFormat32bppBGRA }); + + frame.Initialize(bag); + } + else if (ctx.Settings.SaveFormat == FileFormat.Tiff) + { + Encoder = AddRef(Wic.CreateEncoder(Consts.GUID_ContainerFormatTiff, null)); + Encoder.Initialize(stm, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); + + var bag = (IPropertyBag2)null; + Encoder.CreateNewFrame(out frame, ref bag); + AddRef(frame); + AddRef(bag); + + var props = new PROPBAG2[] { new PROPBAG2 { pstrName = "TiffCompressionMethod" } }; + bag.Write(1, props, new object[] { (byte)WICTiffCompressionOption.WICTiffCompressionNone }); + + frame.Initialize(bag); + } + else + { + Encoder = AddRef(Wic.CreateEncoder(Consts.GUID_ContainerFormatPng, null)); + Encoder.Initialize(stm, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); + + Encoder.CreateNewFrame(out frame, null); + AddRef(frame); + + frame.Initialize(null); + } + + frame.SetResolution(96d, 96d); + frame.SetSize(ctx.Width, ctx.Height); + + if (ctx.Settings.IndexedColor && ctx.PixelFormat == Consts.GUID_WICPixelFormat8bppIndexed) + frame.SetPalette(ctx.DestPalette); + + if (ctx.Metadata?.Count > 0) + { + var metawriter = frame.GetMetadataQueryWriterNoThrow(); + if (metawriter != null) + { + AddRef(metawriter); + foreach (var nv in ctx.Metadata) + metawriter.SetMetadataByNameNoThrow(nv.Key, nv.Value); + } + } + + // TODO setting + //if (ctx.DestColorContext != null) + // frame.SetColorContexts(1, new IWICColorContext[] { ctx.DestColorContext }); + + Frame = frame; + } + + public void WriteSource(WicTransform prev) + { + var src = prev.Source; + + var iformat = src.GetPixelFormat(); + var oformat = iformat; + + Frame.SetPixelFormat(ref oformat); + if (oformat != iformat) + { + // TODO grey -> indexed support + //var pal = AddRef(Wic.CreatePalette()); + //pal.InitializePredefined(WICBitmapPaletteType.WICBitmapPaletteTypeFixedGray256, false); + var conv = AddRef(Wic.CreateFormatConverter()); + conv.Initialize(src, oformat, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom); + src = conv; + } + + Frame.WriteSource(src, null); + + Frame.Commit(); + Encoder.Commit(); + } + } +} \ No newline at end of file diff --git a/src/WIC/WicFormatConverters.cs b/src/WIC/WicFormatConverters.cs new file mode 100644 index 00000000..8aa44147 --- /dev/null +++ b/src/WIC/WicFormatConverters.cs @@ -0,0 +1,196 @@ +using System; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal abstract class WicFormatConverterBase : WicBitmapSourceBase + { + protected byte[] LineBuff; + protected Guid OutFormat; + protected bool HasAlpha; + + public override Guid GetPixelFormat() + { + return OutFormat; + } + + public WicFormatConverterBase(IWICBitmapSource source, Guid dstFormat) : base(source) + { + OutFormat = dstFormat; + LineBuff = new byte[Stride]; + HasAlpha = Format == Consts.GUID_WICPixelFormat64bppBGRA || Format == Consts.GUID_WICPixelFormat32bppBGRA; + } + } + + internal class WicLinearFormatConverter : WicFormatConverterBase + { + public WicLinearFormatConverter(IWICBitmapSource source, Guid dstFormat) : base(source, dstFormat) { } + + unsafe public override void CopyPixels(WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer) + { + fixed (byte* bstart = LineBuff) + fixed (ushort* igtstart = LookupTables.InverseGamma, atstart = LookupTables.Alpha) + { + int oh = prc.Height, oy = prc.Y; + + prc.Height = 1; + for (int y = 0; y < oh; y++) + { + prc.Y = oy + y; + Source.CopyPixels(prc, Stride, Stride, (IntPtr)bstart); + + ushort* op = (ushort*)pbBuffer + y * cbStride; + if (HasAlpha) + mapValuesWithAlpha(bstart, op, igtstart, atstart, (uint)prc.Width * Bpp); + else + mapValues(bstart, op, igtstart, (uint)prc.Width * Bpp); + } + } + } + + unsafe private static void mapValues(byte* ipstart, ushort* opstart, ushort* igtstart, uint len) + { + byte* ip = ipstart + 8, ipe = ipstart + len; + ushort* op = opstart + 8, igt = igtstart; + + while (ip < ipe) + { + ushort o0 = igt[ip[-8]]; + ushort o1 = igt[ip[-7]]; + ushort o2 = igt[ip[-6]]; + ushort o3 = igt[ip[-5]]; + op[-8] = o0; + op[-7] = o1; + op[-6] = o2; + op[-5] = o3; + + o0 = igt[ip[-4]]; + o1 = igt[ip[-3]]; + o2 = igt[ip[-2]]; + o3 = igt[ip[-1]]; + op[-4] = o0; + op[-3] = o1; + op[-2] = o2; + op[-1] = o3; + + op += 8; + ip += 8; + } + + ip -= 8; + op -= 8; + while (ip < ipe) + { + op[0] = igt[ip[0]]; + ip++; + op++; + } + } + + unsafe private static void mapValuesWithAlpha(byte* ipstart, ushort* opstart, ushort* igtstart, ushort* atstart, uint len) + { + byte* ip = ipstart + 4, ipe = ip + len; + ushort* op = opstart + 4, igt = igtstart, at = atstart; + + while (ip < ipe) + { + ushort o0 = igt[ip[-4]]; + ushort o1 = igt[ip[-3]]; + ushort o2 = igt[ip[-2]]; + ushort o3 = at[ip[-1]]; + op[-4] = o0; + op[-3] = o1; + op[-2] = o2; + op[-1] = o3; + op += 4; + ip += 4; + } + } + } + + internal class WicGammaFormatConverter : WicFormatConverterBase + { + public WicGammaFormatConverter(IWICBitmapSource source, Guid dstFormat) : base(source, dstFormat) { } + + unsafe public override void CopyPixels(WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer) + { + fixed (byte* bstart = LineBuff, gtstart = LookupTables.Gamma) + { + int oh = prc.Height, oy = prc.Y; + + prc.Height = 1; + for (int y = 0; y < oh; y++) + { + prc.Y = oy + y; + Source.CopyPixels(prc, Stride, Stride, (IntPtr)bstart); + + byte* op = (byte*)pbBuffer + y * cbStride; + if (HasAlpha) + mapValuesWithAlpha((ushort*)(void*)bstart, op, gtstart, (uint)prc.Width * Bpp / 2); + else + mapValues((ushort*)(void*)bstart, op, gtstart, (uint)prc.Width * Bpp / 2); + } + } + } + + unsafe private static void mapValues(ushort* ipstart, byte* opstart, byte* gtstart, uint len) + { + ushort* ip = ipstart + 8, ipe = ipstart + len; + byte* op = opstart + 8, gt = gtstart; + + while (ip < ipe) + { + byte o0 = gt[ip[-8]]; + byte o1 = gt[ip[-7]]; + byte o2 = gt[ip[-6]]; + byte o3 = gt[ip[-5]]; + op[-8] = o0; + op[-7] = o1; + op[-6] = o2; + op[-5] = o3; + + o0 = gt[ip[-4]]; + o1 = gt[ip[-3]]; + o2 = gt[ip[-2]]; + o3 = gt[ip[-1]]; + op[-4] = o0; + op[-3] = o1; + op[-2] = o2; + op[-1] = o3; + + op += 8; + ip += 8; + } + + ip -= 8; + op -= 8; + while (ip < ipe) + { + op[0] = gt[ip[0]]; + ip++; + op++; + } + } + + unsafe private static void mapValuesWithAlpha(ushort* ipstart, byte* opstart, byte* gtstart, uint len) + { + ushort* ip = ipstart + 4, ipe = ip + len; + byte* op = opstart + 4, gt = gtstart; + + while (ip < ipe) + { + byte o0 = gt[ip[-4]]; + byte o1 = gt[ip[-3]]; + byte o2 = gt[ip[-2]]; + byte o3 = (byte)Math.Min(MathUtil.UnscaleToInt32(ip[-1] << 8), 255); + op[-4] = o0; + op[-3] = o1; + op[-2] = o2; + op[-1] = o3; + op += 4; + ip += 4; + } + } + } +} diff --git a/src/WIC/WicImageProcessor.cs b/src/WIC/WicImageProcessor.cs new file mode 100644 index 00000000..92135be4 --- /dev/null +++ b/src/WIC/WicImageProcessor.cs @@ -0,0 +1,55 @@ +using System.IO; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + public static class WicImageProcessor + { + public static void ProcessImage(string imgPath, Stream ostm, ProcessImageSettings s) + { + using (var ctx = new WicProcessingContext(s)) + using (var dec = new WicDecoder(imgPath, ctx)) + processImage(dec, ctx, ostm); + } + + public static void ProcessImage(byte[] imgBuffer, Stream ostm, ProcessImageSettings s) + { + using (var ctx = new WicProcessingContext(s)) + using (var dec = new WicDecoder(imgBuffer, ctx)) + processImage(dec, ctx, ostm); + } + + public static void ProcessImage(Stream istm, Stream ostm, ProcessImageSettings s) + { + using (var ctx = new WicProcessingContext(s)) + using (var dec = new WicDecoder(istm, ctx)) + processImage(dec, ctx, ostm); + } + + private static void processImage(WicDecoder dec, WicProcessingContext ctx, Stream ostm) + { + using (var frm = new WicFrameReader(dec, ctx)) + using (var met = new WicMetadataReader(frm)) + { + if (!ctx.Settings.Normalized) + ctx.Settings.Fixup((int)ctx.Width, (int)ctx.Height, ctx.IsRotated90); + + ctx.Settings.HybridMode = HybridScaleMode.Turbo; + //ctx.NeedsCache = true; + + using (var qsc = new WicNativeScaler(met)) + using (var rot = new WicExifRotator(qsc)) + using (var cac = new WicConditionalCache(rot)) + using (var crp = new WicCropper(cac)) + using (var pix = new WicPixelFormatConverter(crp)) + using (var res = new WicScaler(pix)) + using (var csc = new WicColorspaceConverter(res)) + using (var mat = new WicMatteTransform(csc)) + using (var pal = new WicPaletizer(mat, 256)) + using (var enc = new WicEncoder(ostm.AsIStream(), ctx)) + enc.WriteSource(pal); + } + } + } +} \ No newline at end of file diff --git a/src/WIC/WicMatte.cs b/src/WIC/WicMatte.cs new file mode 100644 index 00000000..63aa3a1e --- /dev/null +++ b/src/WIC/WicMatte.cs @@ -0,0 +1,131 @@ +using System; + +using PhotoSauce.MagicScaler.Interop; +using static PhotoSauce.MagicScaler.MathUtil; + +namespace PhotoSauce.MagicScaler +{ + internal class WicMatte : WicBitmapSourceBase + { + byte MaskRed, MaskGreen, MaskBlue; + ushort MaskRedLinear, MaskGreenLinear, MaskBlueLinear; + + public WicMatte(IWICBitmapSource source, System.Drawing.Color color) : base(source) + { + if (Format != Consts.GUID_WICPixelFormat32bppBGRA && Format != Consts.GUID_WICPixelFormat64bppBGRA) + throw new NotSupportedException("Pixel format not supported. Must be BGRA"); + + MaskRed = color.R; + MaskGreen = color.G; + MaskBlue = color.B; + + var igt = LookupTables.InverseGamma; + + MaskRedLinear = igt[MaskRed]; + MaskGreenLinear = igt[MaskGreen]; + MaskBlueLinear = igt[MaskBlue]; + } + + unsafe public override void CopyPixels(WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer) + { + Source.CopyPixels(prc, cbStride, cbBufferSize, pbBuffer); + + if (Format == Consts.GUID_WICPixelFormat64bppBGRA) + { + applyMatteLinear(prc, (ushort*)pbBuffer, (int)(cbStride / 2)); + } + else + { + fixed (ushort* igtstart = LookupTables.InverseGamma, atstart = LookupTables.Alpha) + fixed (byte* gtstart = LookupTables.Gamma) + applyMatteGamma(prc, igtstart, gtstart, atstart, (byte*)pbBuffer, (int)cbStride); + } + } + + unsafe private void applyMatteLinear(WICRect prc, ushort* pixels, int stride) + { + const ushort maxalpha = IntScale; + ushort mrl = MaskRedLinear, mgl = MaskGreenLinear, mbl = MaskBlueLinear; + + for (int y = 0; y < prc.Height; y++) + { + ushort* ip = pixels + y * stride; + ushort* ipe = ip + prc.Width * Channels; + + while (ip < ipe) + { + int alpha = ip[3]; + if (alpha == 0) + { + ip[0] = mbl; + ip[1] = mgl; + ip[2] = mrl; + ip[3] = maxalpha; + } + else if (alpha < IntScale) + { + int ia = alpha, ma = maxalpha - ia; + ushort ib = ip[0]; + ushort ig = ip[1]; + ushort ir = ip[2]; + + ib = UnscaleToUInt15(ib * ia + mbl * ma); + ig = UnscaleToUInt15(ig * ia + mgl * ma); + ir = UnscaleToUInt15(ir * ia + mrl * ma); + + ip[0] = ib; + ip[1] = ig; + ip[2] = ir; + ip[3] = maxalpha; + } + + ip += 4; + } + } + } + + unsafe private void applyMatteGamma(WICRect prc, ushort* igtstart, byte* gtstart, ushort* atstart, byte* pixels, int stride) + { + const byte maxalpha = byte.MaxValue; + + byte* gt = gtstart; + ushort* igt = igtstart, at = atstart; + + for (int y = 0; y < prc.Height; y++) + { + byte* ip = pixels + y * stride; + byte* ipe = ip + prc.Width * Channels; + + while (ip < ipe) + { + int alpha = ip[3]; + if (alpha == 0) + { + ip[0] = MaskBlue; + ip[1] = MaskGreen; + ip[2] = MaskRed; + ip[3] = maxalpha; + } + else if (alpha < maxalpha) + { + int ia = at[alpha], ma = IntScale - ia; + int ib = igt[ip[0]]; + int ig = igt[ip[1]]; + int ir = igt[ip[2]]; + + ib = UnscaleToUInt15(ib * ia + MaskBlueLinear * ma); + ig = UnscaleToUInt15(ig * ia + MaskGreenLinear * ma); + ir = UnscaleToUInt15(ir * ia + MaskRedLinear * ma); + + ip[0] = gt[ib]; + ip[1] = gt[ig]; + ip[2] = gt[ir]; + ip[3] = maxalpha; + } + + ip += 4; + } + } + } + } +} diff --git a/src/WIC/WicPlanarHelpers.cs b/src/WIC/WicPlanarHelpers.cs new file mode 100644 index 00000000..1e58bb56 --- /dev/null +++ b/src/WIC/WicPlanarHelpers.cs @@ -0,0 +1,112 @@ +using System; +using System.Diagnostics.Contracts; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal enum WicPlane { Luma, Chroma } + + internal abstract class WicPlanarTransform : WicBase + { + public WicProcessingContext Context { get; protected set; } + public IWICBitmapFrameDecode Frame { get; protected set; } + public IWICBitmapSource SourceY { get; protected set; } + public IWICBitmapSource SourceCbCr { get; protected set; } + + protected WicPlanarTransform(WicTransform prev) + { + Context = prev.Context; + Frame = prev.Frame; + } + } + + internal class WicPlanarCache : WicPlanarTransform + { + public WicPlanarCache(WicTransform prev) : base(prev) + { + Contract.Requires(prev.Source is IWICPlanarBitmapSourceTransform, "Transform chain doesn't support planar mode. Only JPEG Decoder, Rotator, Scaler, and ColorSpaceConverter are allowed"); + var trans = (IWICPlanarBitmapSourceTransform)prev.Source; + + double rat = Context.Settings.HybridScaleRatio.Clamp(1d, 8d); + Context.Width = (uint)Math.Ceiling(Context.Width / rat); + Context.Height = (uint)Math.Ceiling(Context.Height / rat); + + var prog = Frame as IWICProgressiveLevelControl; + if (prog != null) // TODO needs work + { + uint levels = prog.GetLevelCount(); + uint level = (uint)Math.Ceiling(levels / rat) + (Context.Settings.HybridMode == HybridScaleMode.FavorQuality || Context.Settings.HybridMode == HybridScaleMode.Off ? (uint)Math.Ceiling(levels / 8d) : 0u); + prog.SetCurrentLevel(Math.Min(level, levels - (Context.Settings.ScaleRatio >= 2d && levels > 7u ? 2u : 1u))); + } + + var fmts = new Guid[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; + var desc = new WICBitmapPlaneDescription[2]; + + if (!trans.DoesSupportTransform(ref Context.Width, ref Context.Height, Context.TransformOptions, WICPlanarOptions.WICPlanarOptionsPreserveSubsampling, fmts, desc, 2)) + throw new NotSupportedException("Planar Transform not supported"); + + var crop = new WICRect() { X = Context.Settings.Crop.X, Y = Context.Settings.Crop.Y, Width = Context.Settings.Crop.Width, Height = Context.Settings.Crop.Height }; + var source = new WicPlanarCacheSource(trans, desc[0], desc[1], crop, Context.TransformOptions, Context.Width, Context.Height, rat); + + SourceY = source.GetPlane(WicPlane.Luma); + SourceCbCr = source.GetPlane(WicPlane.Chroma); + } + } + + internal class WicPlanarSplitter : WicTransform + { + public WicPlanarSplitter(WicPlanarTransform prev, WicPlane plane) + { + Context = prev.Context; + Frame = prev.Frame; + Source = plane == WicPlane.Luma ? prev.SourceY : prev.SourceCbCr; + + Source.GetSize(out Context.Width, out Context.Height); + } + } + + internal class WicPlanarConverter : WicTransform + { + public WicPlanarConverter(WicTransform prevY, WicTransform prevCbCr) : base(prevY) + { + var cfmt = Consts.GUID_WICPixelFormat24bppBGR; + var conv = AddRef(Wic.CreateFormatConverter()); + var pconv = conv as IWICPlanarFormatConverter; + pconv.Initialize(new IWICBitmapSource[] { prevY.Source, prevCbCr.Source }, 2, cfmt, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom); + Source = pconv; + + if (Context.SourceColorContext != null) + { + var trans = AddRef(Wic.CreateColorTransform()); + trans.Initialize(Source, Context.SourceColorContext, Context.DestColorContext, cfmt); + Source = trans; + } + } + } + + internal class WicPlanarEncoder : WicBase + { + public IWICBitmapEncoder Encoder { get; private set; } + public IWICBitmapFrameEncode Frame { get; private set; } + public IWICPlanarBitmapFrameEncode PlanarFrame { get; private set; } + + public WicPlanarEncoder(WicEncoder enc) + { + Encoder = enc.Encoder; + Frame = enc.Frame; + PlanarFrame = enc.Frame as IWICPlanarBitmapFrameEncode; + } + + public void WriteSource(WicTransform prevY, WicTransform prevCbCr) + { + var oformat = Consts.GUID_WICPixelFormat24bppBGR; + Frame.SetPixelFormat(ref oformat); + + PlanarFrame.WriteSource(new IWICBitmapSource[] { prevY.Source, prevCbCr.Source }, 2, null); + + Frame.Commit(); + Encoder.Commit(); + } + } +} \ No newline at end of file diff --git a/src/WIC/WicPlanarSource.cs b/src/WIC/WicPlanarSource.cs new file mode 100644 index 00000000..dfbb7f7f --- /dev/null +++ b/src/WIC/WicPlanarSource.cs @@ -0,0 +1,225 @@ +using System; +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal class WicPlanarCacheSource + { + private byte[] lineBuffY = null; + private byte[] lineBuffC = null; + private IWICPlanarBitmapSourceTransform sourceTransform; + private WICBitmapTransformOptions sourceTransformOptions; + private WICBitmapPlaneDescription planeDescriptionY; + private WICBitmapPlaneDescription planeDescriptionC; + private WICRect scaledCrop; + private WicPlanarSource sourceY; + private WicPlanarSource sourceC; + private double subsampleRatioX; + private double subsampleRatioY; + private uint scaledWidth; + private uint scaledHeight; + private uint strideY; + private uint strideC; + private uint buffHeightY; + private uint buffHeightC; + private int startY = -1, posY = 0; + private int startC = -1, posC = 0; + + public WicPlanarCacheSource(IWICPlanarBitmapSourceTransform source, WICBitmapPlaneDescription descY, WICBitmapPlaneDescription descC, WICRect crop, WICBitmapTransformOptions transformOptions, uint width, uint height, double ratio) + { + // TODO fractional ratio support? + subsampleRatioX = Math.Ceiling((double)descY.Width / descC.Width); + subsampleRatioY = Math.Ceiling((double)descY.Height / descC.Height); + + var scrop = new WICRect(); + scrop.X = (int)Math.Floor(crop.X / ratio); + scrop.Y = (int)Math.Floor(crop.Y / ratio); + scrop.Width = Math.Min((int)Math.Ceiling(crop.Width / ratio), (int)descY.Width); + scrop.Height = Math.Min((int)Math.Ceiling(crop.Height / ratio), (int)descY.Height); + + if (subsampleRatioX > 1d) + { + descC.Width = Math.Min((uint)Math.Ceiling(scrop.Width / subsampleRatioX), descC.Width); + descY.Width = (uint)Math.Min(descC.Width * subsampleRatioX, scrop.Width); + + if (scrop.X % subsampleRatioX > 0) + scrop.X = (int)(scrop.X / subsampleRatioX) * (int)subsampleRatioX; + } + else + { + descC.Width = descY.Width = (uint)scrop.Width; + } + + if (subsampleRatioY > 1d) + { + descC.Height = Math.Min((uint)Math.Ceiling(scrop.Height / subsampleRatioY), descC.Height); + descY.Height = (uint)Math.Min(descC.Height * subsampleRatioY, scrop.Height); + + if (scrop.Y % subsampleRatioY > 0) + scrop.Y = (int)(scrop.Y / subsampleRatioY) * (int)subsampleRatioY; + } + else + { + descC.Height = descY.Height = (uint)scrop.Height; + } + + sourceTransform = source; + sourceTransformOptions = transformOptions; + planeDescriptionY = descY; + planeDescriptionC = descC; + scaledCrop = scrop; + scaledWidth = width; + scaledHeight = height; + + strideY = (uint)scrop.Width + 3u & ~3u; + buffHeightY = 16u; + + strideC = (uint)(Math.Ceiling(scrop.Width / subsampleRatioX)) * 2u + 3u & ~3u; + buffHeightC = (uint)(buffHeightY / subsampleRatioX); + + sourceY = new WicPlanarSource(this, WicPlane.Luma, descY); + sourceC = new WicPlanarSource(this, WicPlane.Chroma, descC); + } + + unsafe private void loadBuffer(byte* pBuffY, byte* pBuffC, WICRect rect) + { + var planes = new WICBitmapPlane[2]; + planes[0].Format = sourceY.GetPixelFormat(); + planes[1].Format = sourceC.GetPixelFormat(); + planes[0].cbStride = strideY; + planes[1].cbStride = strideC; + planes[0].cbBufferSize = (uint)lineBuffY.Length; + planes[1].cbBufferSize = (uint)lineBuffC.Length; + planes[0].pbBuffer = (IntPtr)pBuffY; + planes[1].pbBuffer = (IntPtr)pBuffC; + + sourceTransform.CopyPixels(rect, scaledWidth, scaledHeight, sourceTransformOptions, WICPlanarOptions.WICPlanarOptionsPreserveSubsampling, planes, 2); + } + + unsafe public void CopyPixels(WicPlane plane, WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer) + { + var load = (lineBuffY == null || (plane == WicPlane.Luma && prc.Y + prc.Height > startY + buffHeightY) || (plane == WicPlane.Chroma && prc.Y + prc.Height > startC + buffHeightC)); + + if (load && (lineBuffY == null || posY < buffHeightY / 4 || posC < buffHeightC / 4)) + { + if (lineBuffY != null) + { + buffHeightY *= 2; + buffHeightC *= 2; + } + + var tbuffY = new byte[strideY * buffHeightY]; + var tbuffC = new byte[strideC * buffHeightC]; + + if (lineBuffY != null) + { + fixed (byte* ptbuffY = tbuffY, ptbuffC = tbuffC, pcbuffY = lineBuffY, pcbuffC = lineBuffC) + { + Buffer.MemoryCopy(pcbuffY, ptbuffY, tbuffY.Length, lineBuffY.Length); + Buffer.MemoryCopy(pcbuffC, ptbuffC, tbuffC.Length, lineBuffC.Length); + + var rect = new WICRect { X = scaledCrop.X, Y = scaledCrop.Y + startY + posY, Width = scaledCrop.Width, Height = Math.Min((int)buffHeightY - posY, scaledCrop.Height - prc.Y) }; + loadBuffer(ptbuffY + (buffHeightY / 2) * strideY, ptbuffC + (buffHeightC / 2) * strideC, rect); + } + + load = false; + } + + lineBuffY = tbuffY; + lineBuffC = tbuffC; + } + + fixed (byte* pBuffY = lineBuffY, pBuffC = lineBuffC) + { + if (load) + { + int offsY = 0, offsC = 0; + if (startY == -1) + { + startY = prc.Y; + if (startY % subsampleRatioY > 0) + startY = (int)(startY / subsampleRatioY) * (int)subsampleRatioY; + + startC = (int)(startY / subsampleRatioY); + posY = prc.Y - startY; + posC = (int)(posY / subsampleRatioY); + } + else if (posY < buffHeightY || posC < buffHeightC) + { + int posMin = (int)Math.Min(posC * subsampleRatioY, posY); + int posMins = (int)(posMin / subsampleRatioY); + posMin = posMins * (int)subsampleRatioY; + + Buffer.MemoryCopy(pBuffY + posMin * strideY, pBuffY, lineBuffY.Length, (buffHeightY - posMin) * strideY); + Buffer.MemoryCopy(pBuffC + posMins * strideC, pBuffC, lineBuffC.Length, (buffHeightC - posMins) * strideC); + + startY += posMin; + startC += posMins; + + posY -= posMin; + posC -= posMins; + + offsY = (int)buffHeightY - posMin; + offsC = (int)buffHeightC - posMins; + } + else + { + startY += (int)buffHeightY; + startC += (int)buffHeightC; + posY = posC = 0; + } + + var rect = new WICRect { X = scaledCrop.X, Y = scaledCrop.Y + startY + posY, Width = scaledCrop.Width, Height = Math.Min((int)buffHeightY - posY, scaledCrop.Height - prc.Y) }; + loadBuffer(pBuffY + offsY * strideY, pBuffC + offsC * strideC, rect); + } + + if (plane == WicPlane.Luma) + { + Buffer.MemoryCopy(pBuffY + (posY * strideY), (void*)pbBuffer, cbBufferSize, cbBufferSize); + posY += prc.Height; + } + else + { + Buffer.MemoryCopy(pBuffC + (posC * strideC), (void*)pbBuffer, cbBufferSize, cbBufferSize); + posC += prc.Height; + } + } + } + + public IWICBitmapSource GetPlane(WicPlane plane) + { + return plane == WicPlane.Luma ? sourceY : sourceC; + } + } + + internal class WicPlanarSource : WicBitmapSourceBase + { + private WicPlanarCacheSource cacheSource; + private WicPlane cachePlane; + + public WicPlanarSource(WicPlanarCacheSource cache, WicPlane plane, WICBitmapPlaneDescription planeDesc) + { + Width = planeDesc.Width; + Height = planeDesc.Height; + Format = planeDesc.Format; + + cacheSource = cache; + cachePlane = plane; + } + + public override void CopyPixels(WICRect prc, uint cbStride, uint cbBufferSize, IntPtr pbBuffer) + { + cacheSource.CopyPixels(cachePlane, prc, cbStride, cbBufferSize, pbBuffer); + } + + public override void GetResolution(out double pDpiX, out double pDpiY) + { + pDpiX = pDpiY = 96d; + } + + public override void CopyPalette(IWICPalette pIPalette) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/WIC/WicTransform.cs b/src/WIC/WicTransform.cs new file mode 100644 index 00000000..2e6c71db --- /dev/null +++ b/src/WIC/WicTransform.cs @@ -0,0 +1,473 @@ +using System; +using System.Linq; +using System.Drawing; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using PhotoSauce.MagicScaler.Interop; + +namespace PhotoSauce.MagicScaler +{ + internal class WicTransform : WicBase + { + public WicProcessingContext Context { get; protected set; } + public IWICBitmapFrameDecode Frame { get; protected set; } + public IWICBitmapSource Source { get; protected set; } + + protected WicTransform() { } + + public WicTransform(WicTransform prev) + { + Frame = prev.Frame; + Source = prev.Source; + Context = prev.Context; + } + + public WicTransform(IWICBitmapFrameDecode frm, IWICBitmapSource src, WicProcessingContext ctx) + { + Frame = frm; + Source = src; + Context = ctx; + } + } + + internal class WicFrameReader : WicTransform + { + public WicFrameReader(WicDecoder dec, WicProcessingContext ctx) + { + Frame = AddRef(dec.Decoder.GetFrame((uint)ctx.Settings.FrameIndex)); + Source = Frame; + Context = ctx; + + if (ctx.ContainerFormat == Consts.GUID_ContainerFormatRaw && ctx.Settings.FrameIndex == 0) + try { Source = AddRef(dec.Decoder.GetPreview()); } catch { } + + ctx.PixelFormat = Source.GetPixelFormat(); + Source.GetSize(out ctx.Width, out ctx.Height); + + var ptrans = Source as IWICPlanarBitmapSourceTransform; + if (ptrans != null) + { + uint pw = ctx.Width, ph = ctx.Height; + var pdesc = new WICBitmapPlaneDescription[2]; + var pfmts = new Guid[] { Consts.GUID_WICPixelFormat8bppY, Consts.GUID_WICPixelFormat16bppCbCr }; + ctx.SupportsPlanar = ptrans.DoesSupportTransform(ref pw, ref ph, WICBitmapTransformOptions.WICBitmapTransformRotate0, WICPlanarOptions.WICPlanarOptionsPreserveSubsampling, pfmts, pdesc, 2); + ctx.IsSubsampled = pdesc[0].Width != pdesc[1].Width || pdesc[0].Height != pdesc[1].Height; + } + } + } + + internal class WicMetadataReader : WicTransform + { + private static IWICColorContext getDefaultColorProfile(Guid pixelFormat) + { + var pfi = Wic.CreateComponentInfo(pixelFormat) as IWICPixelFormatInfo; + var cc = pfi.GetColorContext(); + Marshal.ReleaseComObject(pfi); + return cc; + } + + private static readonly Lazy cmykProfile = new Lazy(() => getDefaultColorProfile(Consts.GUID_WICPixelFormat32bppCMYK)); + private static readonly Lazy srgbProfile = new Lazy(() => getDefaultColorProfile(Consts.GUID_WICPixelFormat24bppBGR)); + + public WicMetadataReader(WicTransform prev, bool basicOnly = false) : base(prev) + { + var pfi = AddRef(Wic.CreateComponentInfo(Context.PixelFormat) as IWICPixelFormatInfo2); + if (pfi.GetNumericRepresentation() == WICPixelFormatNumericRepresentation.WICPixelFormatNumericRepresentationIndexed) + { + var pal = AddRef(Wic.CreatePalette()); + Frame.CopyPalette(pal); + + Context.HasAlpha = pal.HasAlpha(); + Context.IsGreyscale = pal.IsGrayscale(); + } + else + { + uint chans = pfi.GetChannelCount(); + bool trans = pfi.SupportsTransparency(); + Context.HasAlpha = trans; + Context.IsGreyscale = chans == 1u; + Context.IsCmyk = (chans == 4u && !trans) || (chans == 5u && trans); + } + + var metareader = Frame.GetMetadataQueryReaderNoThrow(); + if (metareader != null) + { + AddRef(metareader); + // Exif orientation + if (metareader.HasMetadataName("System.Photo.Orientation")) + { + ushort orientation = 1; + var ovar = new PropVariant(); + metareader.GetMetadataByName("System.Photo.Orientation", ovar); + if (ovar.UnmanagedType == VarEnum.VT_UI2) + orientation = (ushort)ovar.Value; + + var opt = WICBitmapTransformOptions.WICBitmapTransformRotate0; + if (orientation == 3 || orientation == 4) + opt = WICBitmapTransformOptions.WICBitmapTransformRotate180; + else if (orientation == 6 || orientation == 7) + opt = WICBitmapTransformOptions.WICBitmapTransformRotate90; + else if (orientation == 5 || orientation == 8) + opt = WICBitmapTransformOptions.WICBitmapTransformRotate270; + + if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7) + opt |= WICBitmapTransformOptions.WICBitmapTransformFlipHorizontal; + + Context.TransformOptions = opt; + } + + if (basicOnly) + return; + + // other requested properties + var propdic = new Dictionary(); + foreach (string prop in Context.Settings.MetadataNames ?? Enumerable.Empty()) + { + if (metareader.HasMetadataName(prop)) + { + var pvar = new PropVariant(); + metareader.GetMetadataByName(prop, pvar); + if (pvar.Value != null) + propdic[prop] = pvar; + } + } + + Context.Metadata = propdic; + } + + // ICC profiles + //http://ninedegreesbelow.com/photography/embedded-color-space-information.html + uint ccc = Frame.GetColorContextCount(); + var profiles = new IWICColorContext[ccc]; + var profile = (IWICColorContext)null; + + if (ccc > 0) + { + for (int i = 0; i < ccc; i++) + profiles[i] = AddRef(Wic.CreateColorContext()); + + Frame.GetColorContexts(ccc, profiles); + } + + foreach (var cc in profiles) + { + var cct = cc.GetType(); + if (cct == WICColorContextType.WICColorContextProfile) + { + uint ccs = cc.GetProfileBytes(0, null); + var ccb = new byte[ccs]; + cc.GetProfileBytes(ccs, ccb); + + // match only color profiles that match our intended use. if we have a standard sRGB profile, don't save it; we don't need to convert + var cp = new ColorProfileInfo(ccb); + if (cp.IsValid && ((cp.IsDisplayRgb && !cp.IsStandardSrgb) || (Context.IsCmyk && cp.IsCmyk) /* || (Context.IsGreyscale && cp.DataColorSpace == "GRAY") */)) + { + profile = cc; + break; + } + } + else if (cct == WICColorContextType.WICColorContextExifColorSpace && cc.GetExifColorSpace() == ExifColorSpace.AdobeRGB) + { + profile = cc; + break; + } + } + + Context.SourceColorContext = profile ?? (Context.IsCmyk ? cmykProfile.Value : null); + Context.DestColorContext = srgbProfile.Value; + } + } + + internal class WicConditionalCache : WicTransform + { + public WicConditionalCache(WicTransform prev) : base(prev) + { + if (!Context.NeedsCache) + return; + + var crop = Context.Settings.Crop; + var bmp = AddRef(Wic.CreateBitmapFromSourceRect(Source, (uint)crop.X, (uint)crop.Y, (uint)crop.Width, (uint)crop.Height)); + + Source = bmp; + Source.GetSize(out Context.Width, out Context.Height); + Context.Settings.Crop = new Rectangle(0, 0, (int)Context.Width, (int)Context.Height); + Context.NeedsCache = false; + } + } + + internal class WicCmykConverter : WicTransform + { + public WicCmykConverter(WicTransform prev) : base(prev) + { + if (!Context.IsCmyk) + return; + + // TODO 40bppcmyka unsupported -- WIC bug + var trans = AddRef(Wic.CreateColorTransform()); + trans.Initialize(Source, Context.SourceColorContext, Context.DestColorContext, Context.HasAlpha ? Consts.GUID_WICPixelFormat32bppBGRA : Consts.GUID_WICPixelFormat24bppBGR); + + Source = trans; + Context.SourceColorContext = null; + } + } + + internal class WicColorspaceConverter : WicTransform + { + public WicColorspaceConverter(WicTransform prev) : base(prev) + { + if (Context.SourceColorContext == null) + return; + + var trans = AddRef(Wic.CreateColorTransform()); + trans.Initialize(Source, Context.SourceColorContext, Context.DestColorContext, Context.IsCmyk ? Context.HasAlpha ? Consts.GUID_WICPixelFormat32bppBGRA : Consts.GUID_WICPixelFormat24bppBGR : Context.PixelFormat); + + Source = trans; + } + } + + internal class WicPixelFormatConverter : WicTransform + { + public WicPixelFormatConverter(WicTransform prev) : base(prev) + { + var newFormat = Consts.GUID_WICPixelFormat24bppBGR; + if (Context.HasAlpha) + newFormat = Context.PixelFormat == Consts.GUID_WICPixelFormat32bppPBGRA ? Consts.GUID_WICPixelFormat32bppPBGRA : Consts.GUID_WICPixelFormat32bppBGRA; + else if (Context.IsGreyscale) + newFormat = Consts.GUID_WICPixelFormat8bppGray; + else if (Context.IsCmyk && Context.SourceColorContext != null) + newFormat = Consts.GUID_WICPixelFormat32bppCMYK; + + if (Context.PixelFormat == newFormat) + return; + + var conv = AddRef(Wic.CreateFormatConverter()); + if (!conv.CanConvert(Context.PixelFormat, newFormat)) + throw new NotSupportedException("Can't convert to destination pixel format"); + + conv.Initialize(Source, newFormat, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom); + Source = conv; + Context.PixelFormat = Source.GetPixelFormat(); + } + } + + internal class WicPaletizer : WicTransform + { + public WicPaletizer(WicTransform prev, uint colors) : base(prev) + { + var newFormat = Consts.GUID_WICPixelFormat8bppIndexed; + + if (!Context.Settings.IndexedColor || Context.PixelFormat == newFormat) + return; + + var conv = AddRef(Wic.CreateFormatConverter()); + if (!conv.CanConvert(Context.PixelFormat, newFormat)) + throw new ArgumentException("Can't convert to destination pixel format"); + + var bmp = AddRef(Wic.CreateBitmapFromSource(Source, WICBitmapCreateCacheOption.WICBitmapCacheOnDemand)); + + var pal = AddRef(Wic.CreatePalette()); + pal.InitializeFromBitmap(bmp, colors, false); + Context.DestPalette = pal; + + conv.Initialize(bmp, newFormat, WICBitmapDitherType.WICBitmapDitherTypeErrorDiffusion, pal, 1.0, WICBitmapPaletteType.WICBitmapPaletteTypeCustom); + Source = conv; + Context.PixelFormat = Source.GetPixelFormat(); + } + } + + internal class WicExifRotator : WicTransform + { + public WicExifRotator(WicTransform prev) : base(prev) + { + if (Context.TransformOptions == WICBitmapTransformOptions.WICBitmapTransformRotate0) + return; + + var rotator = AddRef(Wic.CreateBitmapFlipRotator()); + rotator.Initialize(Source, Context.TransformOptions); + + Source = rotator; + Source.GetSize(out Context.Width, out Context.Height); + + if (Context.TransformOptions != WICBitmapTransformOptions.WICBitmapTransformFlipHorizontal) + Context.NeedsCache = true; + } + } + + internal class WicCropper : WicTransform + { + public WicCropper(WicTransform prev) : base(prev) + { + var crop = Context.Settings.Crop; + if (crop == Rectangle.FromLTRB(0, 0, (int)Context.Width, (int)Context.Height)) + return; + + var cropper = AddRef(Wic.CreateBitmapClipper()); + cropper.Initialize(Source, new WICRect() { X = crop.X, Y = crop.Y, Width = crop.Width, Height = crop.Height }); + + Source = cropper; + Source.GetSize(out Context.Width, out Context.Height); + } + } + + internal class WicScaler : WicTransform + { + public WicScaler(WicTransform prev, bool hybrid = false) : base(prev) + { + if (Context.Settings.Width == Context.Width && Context.Settings.Height == Context.Height) + return; + + double rat = Context.Settings.HybridScaleRatio; + if (hybrid && rat == 1d) + return; + + if (Source is IWICBitmapSourceTransform) + { + // null crop to disable IWICBitmapSourceTransform scaling + var clip = AddRef(Wic.CreateBitmapClipper()); + clip.Initialize(Source, new WICRect() { X = 0, Y = 0, Width = (int)Context.Width, Height = (int)Context.Height }); + + Source = clip; + } + + uint ow = hybrid ? (uint)Math.Ceiling(Context.Width / rat) : (uint)Context.Settings.Width; + uint oh = hybrid ? (uint)Math.Ceiling(Context.Height / rat) : (uint)Context.Settings.Height; + + var scaler = AddRef(Wic.CreateBitmapScaler()); + scaler.Initialize(Source, ow, oh, Context.Settings.ScaleRatio < 1.1 ? WICBitmapInterpolationMode.WICBitmapInterpolationModeCubic : WICBitmapInterpolationMode.WICBitmapInterpolationModeFant); + + Source = scaler; + Source.GetSize(out Context.Width, out Context.Height); + + if (hybrid) + Context.Settings.Crop = new Rectangle(0, 0, (int)Context.Width, (int)Context.Height); + } + } + + internal class WicNativeScaler : WicTransform + { + public WicNativeScaler(WicTransform prev) : base(prev) + { + double rat = Context.Settings.HybridScaleRatio; + if (rat == 1d) + return; + + var trans = Source as IWICBitmapSourceTransform; + if (trans == null) + return; + + uint ow = Context.Width, oh = Context.Height; + uint cw = (uint)Math.Ceiling(ow / rat), ch = (uint)Math.Ceiling(oh / rat); + trans.GetClosestSize(ref cw, ref ch); + + if (cw == ow && ch == oh) + return; + + double wrat = (double)ow / cw, hrat = (double)oh / ch; + + var crop = Context.Settings.Crop; + Context.Settings.Crop = new Rectangle((int)Math.Floor(crop.X / wrat), (int)Math.Floor(crop.Y / hrat), (int)Math.Ceiling(crop.Width / wrat), (int)Math.Ceiling(crop.Height / hrat)); + + var scaler = AddRef(Wic.CreateBitmapScaler()); + scaler.Initialize(Source, cw, ch, WICBitmapInterpolationMode.WICBitmapInterpolationModeFant); + + Source = scaler; + Source.GetSize(out Context.Width, out Context.Height); + } + } + + internal class WicGammaExpand : WicTransform + { + public WicGammaExpand(WicTransform prev) : base(prev) + { + if (Context.Settings.BlendingMode != GammaMode.Linear) + return; + + var fmt = Source.GetPixelFormat(); + var conv = (WicLinearFormatConverter)null; + + if (fmt == Consts.GUID_WICPixelFormat32bppBGRA) + conv = new WicLinearFormatConverter(Source, Consts.GUID_WICPixelFormat64bppBGRA); + else if (fmt == Consts.GUID_WICPixelFormat24bppBGR) + conv = new WicLinearFormatConverter(Source, Consts.GUID_WICPixelFormat48bppBGR); + else if (fmt == Consts.GUID_WICPixelFormat8bppGray || fmt == Consts.GUID_WICPixelFormat8bppY) + conv = new WicLinearFormatConverter(Source, Consts.GUID_WICPixelFormat16bppGray); + + if (conv == null) + return; + + Source = conv; + Context.PixelFormat = Source.GetPixelFormat(); + } + } + + internal class WicGammaCompress : WicTransform + { + public WicGammaCompress(WicTransform prev) : base(prev) + { + var fmt = Source.GetPixelFormat(); + var conv = (WicGammaFormatConverter)null; + + if (fmt == Consts.GUID_WICPixelFormat64bppBGRA) + conv = new WicGammaFormatConverter(Source, Consts.GUID_WICPixelFormat32bppBGRA); + else if (fmt == Consts.GUID_WICPixelFormat48bppBGR) + conv = new WicGammaFormatConverter(Source, Consts.GUID_WICPixelFormat24bppBGR); + else if (fmt == Consts.GUID_WICPixelFormat16bppGray) + conv = new WicGammaFormatConverter(Source, Context.SupportsPlanar ? Consts.GUID_WICPixelFormat8bppY : Consts.GUID_WICPixelFormat8bppGray); + + if (conv == null) + return; + + Source = conv; + Context.PixelFormat = Source.GetPixelFormat(); + } + } + + internal class WicHighQualityScaler : WicTransform + { + public WicHighQualityScaler(WicTransform prev) : base(prev) + { + uint width = (uint)Context.Settings.Width, height = (uint)Context.Settings.Height; + var interpolatorx = width == Context.Width ? InterpolationSettings.NearestNeighbor : Context.Settings.Interpolation; + var interpolatory = height == Context.Height ? InterpolationSettings.NearestNeighbor : Context.Settings.Interpolation; + + var mapx = KernelMap.MakeScaleMap(Context.Width, width, interpolatorx); + var mapy = KernelMap.MakeScaleMap(Context.Height, height, interpolatory); + + if (Context.Settings.BlendingMode == GammaMode.Linear) + Source = new WicConvolution16bpc(Source, mapx, mapy); + else + Source = new WicConvolution8bpc(Source, mapx, mapy); + + Source.GetSize(out Context.Width, out Context.Height); + } + } + + internal class WicUnsharpMask : WicTransform + { + public WicUnsharpMask(WicTransform prev) : base(prev) + { + var ss = Context.Settings.UnsharpMask; + if (ss.Radius <= 0 || ss.Amount <= 0) + return; + + var mapx = KernelMap.MakeBlurMap(Context.Width, ss.Radius); + var mapy = KernelMap.MakeBlurMap(Context.Height, ss.Radius); + + Source = new WicUnsharpMask8bpc(Source, mapx, mapy, ss); + } + } + + internal class WicMatteTransform : WicTransform + { + public WicMatteTransform(WicTransform prev) : base(prev) + { + if (Context.Settings.MatteColor.IsEmpty || (Context.PixelFormat != Consts.GUID_WICPixelFormat32bppBGRA && Context.PixelFormat != Consts.GUID_WICPixelFormat64bppBGRA)) + return; + + var mat = new WicMatte(Source, Context.Settings.MatteColor); + Source = mat; + } + } + +} \ No newline at end of file