public static byte[] WicResize(IWICComponentFactory factory, IWICBitmapFrameDecode frame, int width, int height, int quality) { // Prepare output stream to cache file var outputStream = new MemoryIStream(); // Prepare PNG encoder var encoder = factory.CreateEncoder(Consts.GUID_ContainerFormatJpeg, null); encoder.Initialize(outputStream, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); // Prepare output frame IWICBitmapFrameEncode outputFrame; var arg = new IPropertyBag2[1]; encoder.CreateNewFrame(out outputFrame, arg); var propBag = arg[0]; var propertyBagOption = new PROPBAG2[1]; propertyBagOption[0].pstrName = "ImageQuality"; propBag.Write(1, propertyBagOption, new object[] { ((float)quality) / 100 }); outputFrame.Initialize(propBag); double dpiX, dpiY; frame.GetResolution(out dpiX, out dpiY); outputFrame.SetResolution(dpiX, dpiY); uint ow, oh, w, h; frame.GetSize(out ow, out oh); if (ow > oh) { w = (uint)width; h = (uint)((double)height * (double)oh / (double)ow); } else { w = (uint)((double)height * (double)ow / (double)oh); h = (uint)height; } outputFrame.SetSize(w, h); // Prepare scaler var scaler = factory.CreateBitmapScaler(); scaler.Initialize(frame, w, h, WICBitmapInterpolationMode.WICBitmapInterpolationModeFant); // Write the scaled source to the output frame outputFrame.WriteSource(scaler, new WICRect { X = 0, Y = 0, Width = (int)width, Height = (int)height }); outputFrame.Commit(); encoder.Commit(); var outputArray = outputStream.ToArray(); outputStream.Close(); Marshal.ReleaseComObject(outputFrame); Marshal.ReleaseComObject(scaler); Marshal.ReleaseComObject(propBag); Marshal.ReleaseComObject(encoder); return(outputArray); }
public static void Write <T>(this IPropertyBag2 bag, string name, T val) where T : unmanaged { var prop = new PROPBAG2 { pstrName = name }; var pvar = new UnmanagedPropVariant(); if (typeof(T) == typeof(bool)) { pvar.vt = VarEnum.VT_BOOL; pvar.int16Value = (bool)(object)val ? (short)-1 : (short)0; } else if (typeof(T) == typeof(byte)) { pvar.vt = VarEnum.VT_UI1; pvar.byteValue = (byte)(object)val; } else if (typeof(T) == typeof(float)) { pvar.vt = VarEnum.VT_R4; pvar.floatValue = (float)(object)val; } else { throw new ArgumentException("Marshaling not implemented for type: " + typeof(T).Name, nameof(T)); } ProxyFunctions.PropertyBagWrite(bag, 1, prop, pvar); }
/// <summary> /// Sets the value of the property with this name /// </summary> /// <param name="name">The name.</param> /// <param name="value">The value.</param> public void Set(string name, object value) { CheckIfInitialized(); // In order to set a property in the property bag // we need to convert the value to the destination type var previousValue = Get(name); value = Convert.ChangeType(value, previousValue == null?value.GetType() : previousValue.GetType()); // Set the property var propbag2 = new PROPBAG2() { Name = name }; var result = nativePropertyBag.Write(1, ref propbag2, value); result.CheckError(); propbag2.Dispose(); }
/// <summary> /// Gets the value of the property with this name. /// </summary> /// <param name="name">The name.</param> /// <returns>Value of the property</returns> public object Get(string name) { CheckIfInitialized(); object value; var propbag2 = new PROPBAG2() { Name = name }; Result error; // Gets the property var result = nativePropertyBag.Read(1, ref propbag2, IntPtr.Zero, out value, out error); if (result.Failure || error.Failure) { throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Property with name [{0}] is not valid for this instance", name)); } propbag2.Dispose(); return(value); }
public static byte[] WicResize(IWICComponentFactory factory, IWICBitmapFrameDecode frame, int width, int height, int quality) { // Prepare output stream to cache file var outputStream = new MemoryIStream(); // Prepare PNG encoder var encoder = factory.CreateEncoder(Consts.GUID_ContainerFormatJpeg, null); encoder.Initialize(outputStream, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); // Prepare output frame IWICBitmapFrameEncode outputFrame; var arg = new IPropertyBag2[1]; encoder.CreateNewFrame(out outputFrame, arg); var propBag = arg[0]; var propertyBagOption = new PROPBAG2[1]; propertyBagOption[0].pstrName = "ImageQuality"; propBag.Write(1, propertyBagOption, new object[] {((float) quality)/100}); outputFrame.Initialize(propBag); double dpiX, dpiY; frame.GetResolution(out dpiX, out dpiY); outputFrame.SetResolution(dpiX, dpiY); uint ow, oh, w, h; frame.GetSize(out ow, out oh); if (ow > oh ) { w = (uint)width; h = (uint)((double)height * (double)oh / (double)ow); } else { w = (uint)((double)height * (double)ow / (double)oh); h = (uint)height; } outputFrame.SetSize(w, h); // Prepare scaler var scaler = factory.CreateBitmapScaler(); scaler.Initialize(frame, w, h, WICBitmapInterpolationMode.WICBitmapInterpolationModeFant); // Write the scaled source to the output frame outputFrame.WriteSource(scaler, new WICRect {X = 0, Y = 0, Width = (int) width, Height = (int) height}); outputFrame.Commit(); encoder.Commit(); var outputArray = outputStream.ToArray(); outputStream.Close(); Marshal.ReleaseComObject(outputFrame); Marshal.ReleaseComObject(scaler); Marshal.ReleaseComObject(propBag); Marshal.ReleaseComObject(encoder); return outputArray; }
public void EncodeToStream(IWICComponentFactory factory, IWICBitmapSource data, IStream outputStream) { //A list of COM objects to destroy List <object> com = new List <object>(); try { //Find the GUID of the destination format Guid guidEncoder = Consts.GUID_ContainerFormatJpeg; //Find out the data's pixel format Guid pFormat = Guid.Empty; data.GetPixelFormat(out pFormat); //Create the encoder var encoder = factory.CreateEncoder(guidEncoder, null); com.Add(encoder); //And initialize it encoder.Initialize(outputStream, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); // Create the output frame and property bag IWICBitmapFrameEncode outputFrame; var propertyBagArray = new IPropertyBag2[1]; encoder.CreateNewFrame(out outputFrame, propertyBagArray); //An array is used instead of an out parameter... I have no idea why com.Add(outputFrame); //The property bag is a COM object... var propBag = propertyBagArray[0]; com.Add(propBag); //Adjust encoder settings if it's a jpegs if (guidEncoder.Equals(Consts.GUID_ContainerFormatJpeg)) { //Configure encoder settings (see http://msdn.microsoft.com/en-us/library/windows/desktop/ee719871(v=vs.85).aspx#encoderoptions) var qualityOption = new PROPBAG2[1]; qualityOption[0].pstrName = "ImageQuality"; qualityOption[0].vt = VarEnum.VT_R4; object qualityValue = ((float)90) / 100; propBag.Write(1, qualityOption, new object[] { qualityValue }); } //Apply the property bag outputFrame.Initialize(propBag); //Get size uint fw, fh; data.GetSize(out fw, out fh); //Set destination frame size outputFrame.SetSize(fw, fh); // Write the data to the output frame outputFrame.WriteSource(data, null); outputFrame.Commit(); encoder.Commit(); } finally { //Manually cleanup all the com reference counts, aggressively while (com.Count > 0) { Marshal.ReleaseComObject(com[com.Count - 1]); //In reverse order, so no item is ever deleted out from under another. com.RemoveAt(com.Count - 1); } } }
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 Resize(byte[] sourceBytes, Stream targetStream, uint targetMaxSize) { var factory = (IWICComponentFactory) new WICImagingFactory(); var inputStream = factory.CreateStream(); inputStream.InitializeFromMemory(sourceBytes, (uint)sourceBytes.Length); var decoder = factory.CreateDecoderFromStream(inputStream, null, WICDecodeOptions.WICDecodeMetadataCacheOnLoad); var frame = decoder.GetFrame(0); // Compute target size uint width, height, targetWidth, targetHeight; frame.GetSize(out width, out height); if (width <= targetMaxSize && height <= targetMaxSize) { targetHeight = height; targetWidth = width; } else if (width > height) { targetWidth = targetMaxSize; targetHeight = height * targetMaxSize / width; } else { targetWidth = width * targetMaxSize / height; targetHeight = targetMaxSize; } // Prepare output stream to cache file var outputStreamAdapter = new StreamAdapter(targetStream); // Prepare JPG encoder var encoder = factory.CreateEncoder(Consts.GUID_ContainerFormatJpeg, null); encoder.Initialize(outputStreamAdapter, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); // Prepare output frame IWICBitmapFrameEncode outputFrame; var argument = new IPropertyBag2[1]; encoder.CreateNewFrame(out outputFrame, argument); var propBag = argument[0]; var propertyBagOption = new PROPBAG2[1]; propertyBagOption[0].pstrName = ImageQualityParamName; propBag.Write(1, propertyBagOption, this.jpegQuality); outputFrame.Initialize(propBag); outputFrame.SetResolution(this.thumbDpi, this.thumbDpi); outputFrame.SetSize(targetWidth, targetHeight); // Prepare scaler var scaler = factory.CreateBitmapScaler(); scaler.Initialize(frame, targetWidth, targetHeight, WICBitmapInterpolationMode.WICBitmapInterpolationModeLinear); // Write the scaled source to the output frame outputFrame.WriteSource(scaler, new WICRect { X = 0, Y = 0, Width = (int)targetWidth, Height = (int)targetHeight }); outputFrame.Commit(); encoder.Commit(); }
public void EncodeToStream(IWICComponentFactory factory, IWICBitmapSource data, Size imageSize, IStream outputStream) { //A list of COM objects to destroy List <object> com = new List <object>(); try { //Find the GUID of the destination format Guid guidEncoder = GetOutputFormatWicGuid(); //Find out the data's pixel format Guid pFormat = Guid.Empty; data.GetPixelFormat(out pFormat); //Create the encoder var encoder = factory.CreateEncoder(guidEncoder, null); com.Add(encoder); //And initialize it encoder.Initialize(outputStream, WICBitmapEncoderCacheOption.WICBitmapEncoderNoCache); // Create the output frame and property bag IWICBitmapFrameEncode outputFrame; var propertyBagArray = new IPropertyBag2[1]; encoder.CreateNewFrame(out outputFrame, propertyBagArray); //An array is used instead of an out parameter... I have no idea why com.Add(outputFrame); //The property bag is a COM object... var propBag = propertyBagArray[0]; com.Add(propBag); //Adjust encoder settings if it's a jpegs if (guidEncoder.Equals(Consts.GUID_ContainerFormatJpeg)) { //Jpeg //ImageQuality 0..1 //"JpegYCrCbSubsampling" WICJpegYCrCbSubsamplingOption. //Configure encoder settings (see http://msdn.microsoft.com/en-us/library/windows/desktop/ee719871(v=vs.85).aspx#encoderoptions) var qualityOption = new PROPBAG2[1]; qualityOption[0].pstrName = "ImageQuality"; propBag.Write(1, qualityOption, new object[] { ((float)Math.Max(0, Math.Min(100, Quality))) / 100 }); WICJpegYCrCbSubsamplingOption subsampling = WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsamplingDefault; //411 NOT SUPPPORTED BY WIC - only by freeimage if ("420".Equals(Subsampling)) { subsampling = WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsampling420; } if ("422".Equals(Subsampling)) { subsampling = WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsampling422; } if ("444".Equals(Subsampling)) { subsampling = WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsampling444; } if (subsampling != WICJpegYCrCbSubsamplingOption.WICJpegYCrCbSubsamplingDefault) { var samplingOption = new PROPBAG2[1]; samplingOption[0].pstrName = "JpegYCrCbSubsampling"; samplingOption[0].vt = VarEnum.VT_UI1; propBag.Write(1, samplingOption, new object[] { (byte)subsampling }); } } //PNG interlace if (guidEncoder.Equals(Consts.GUID_ContainerFormatPng)) { var interlaceOption = new PROPBAG2[1]; interlaceOption[0].pstrName = "InterlaceOption"; propBag.Write(1, interlaceOption, new object[] { Interlace ?? false }); } //Apply the property bag outputFrame.Initialize(propBag); //Convert the bitmap to the correct pixel format for encoding. //Jpeg: encodes as GUID_WICPixelFormat24bppBGR or GUID_WICPixelFormat8bppGray. //If the original pixel format has an alpha chanel, we need to specify a matte color. //UPDATE - IWICFormatConverter doesn't let you specify a matte color. Disabling code if (false && guidEncoder.Equals(Consts.GUID_ContainerFormatJpeg)) { ConversionUtils.HasAlphaAbility(pFormat); var conv = factory.CreateFormatConverter(); com.Add(conv); if (conv.CanConvert(pFormat, Consts.GUID_WICPixelFormat24bppBGR)) { /*dither, pIPalette, alphaThresholdPercent, and paletteTranslate are used to mitigate color loss when * converting to a reduced bit-depth format. For conversions that do not need these settings, the * following parameters values should be used: dither set to WICBitmapDitherTypeNone, pIPalette set to NULL, * alphaThresholdPercent set to 0.0f, and paletteTranslate set to WICBitmapPaletteTypeCustom.*/ conv.Initialize(data, Consts.GUID_WICPixelFormat24bppBGR, WICBitmapDitherType.WICBitmapDitherTypeNone, null, 0.0f, WICBitmapPaletteType.WICBitmapPaletteTypeCustom); data = conv; //Oops, we didn't do anything - there's no way to specify a matte color! } } //GIF encodes as GUID_WICPixelFormat8bppIndexed //If the current format is > 8bpp, quantization may be required, and we may need to manually build the palette with Median Cut. //PNG encodeds as EVERYTHING! Way too many formats supported. // If the user is specifying a colors setting, we need to // convert to GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat2bppIndexed, or GUID_WICPixelFormat1bppIndexed if ((guidEncoder.Equals(Consts.GUID_ContainerFormatPng) && this.Colors != -1) || (guidEncoder.Equals(Consts.GUID_ContainerFormatGif))) { Guid target = Consts.GUID_WICPixelFormat8bppIndexed; int colors = this.Colors; if (colors > 0 && guidEncoder.Equals(Consts.GUID_ContainerFormatPng)) { if (colors <= 2) { target = Consts.GUID_WICPixelFormat1bppIndexed; } if (colors <= 4) { target = Consts.GUID_WICPixelFormat2bppIndexed; } if (colors <= 32) { target = Consts.GUID_WICPixelFormat4bppIndexed; } } if (colors < 0) { colors = 256; } if (colors < 2) { colors = 2; } if (colors > 256) { colors = 256; } var conv = factory.CreateFormatConverter(); com.Add(conv); if (conv.CanConvert(pFormat, target)) { var palette = factory.CreatePalette(); com.Add(palette); palette.InitializeFromBitmap(data, (uint)colors, true); /*dither, pIPalette, alphaThresholdPercent, and paletteTranslate are used to mitigate color loss when * converting to a reduced bit-depth format. For conversions that do not need these settings, the * following parameters values should be used: dither set to WICBitmapDitherTypeNone, pIPalette set to NULL, * alphaThresholdPercent set to 0.0f, and paletteTranslate set to WICBitmapPaletteTypeCustom.*/ conv.Initialize(data, target, this.Dither ? WICBitmapDitherType.WICBitmapDitherTypeErrorDiffusion : WICBitmapDitherType.WICBitmapDitherTypeNone, palette, 50.0f, WICBitmapPaletteType.WICBitmapPaletteTypeCustom); data = conv; } } //Get size uint fw, fh; data.GetSize(out fw, out fh); //Set destination frame size outputFrame.SetSize(fw, fh); // Write the data to the output frame outputFrame.WriteSource(data, null); outputFrame.Commit(); encoder.Commit(); } finally { //Manually cleanup all the com reference counts, aggressively while (com.Count > 0) { Marshal.ReleaseComObject(com[com.Count - 1]); //In reverse order, so no item is ever deleted out from under another. com.RemoveAt(com.Count - 1); } } }