private static Texture DecodeMultiframe(ImagingFactory imagingFactory, WicFlags flags, TextureDescription description, BitmapDecoder decoder) { var texture = new Texture(description); Guid dstFormat = ToWic(description.Format, false); for (int index = 0; index < description.ArraySize; ++index) { var image = texture.Images[index]; using (var frame = decoder.GetFrame(index)) { var pfGuid = frame.PixelFormat; var size = frame.Size; if (size.Width == description.Width && size.Height == description.Height) { // This frame does not need resized if (pfGuid == dstFormat) { frame.CopyPixels(image.Data, image.RowPitch); } else { using (var converter = new FormatConverter(imagingFactory)) { converter.Initialize(frame, dstFormat, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); converter.CopyPixels(image.Data, image.RowPitch); } } } else { // This frame needs resizing using (var scaler = new BitmapScaler(imagingFactory)) { scaler.Initialize(frame, description.Width, description.Height, GetWicInterp(flags)); Guid pfScaler = scaler.PixelFormat; if (pfScaler == dstFormat) { scaler.CopyPixels(image.Data, image.RowPitch); } else { // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we // convert it to our desired format using (var converter = new FormatConverter(imagingFactory)) { converter.Initialize(scaler, dstFormat, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); converter.CopyPixels(image.Data, image.RowPitch); } } } } } } return texture; }
/// <summary> /// Loads the specified image(s). /// </summary> /// <param name="imagingFactory"> /// The factory for creating components for the Windows Imaging Component (WIC). /// </param> /// <param name="stream">The stream to read from.</param> /// <param name="flags">Additional options.</param> /// <returns>A <see cref="Texture"/> representing the image(s).</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="imagingFactory"/>, <paramref name="stream"/> is <see langword="null"/>. /// </exception> public static Texture Load(ImagingFactory imagingFactory, Stream stream, WicFlags flags) { if (imagingFactory == null) { throw new ArgumentNullException("imagingFactory"); } if (stream == null) { throw new ArgumentNullException("stream"); } // Simple version: /* * using (var bitmapDecoder = new BitmapDecoder(_imagingFactory, stream, DecodeOptions.CacheOnDemand)) * { * // Convert the image to pre-multiplied RGBA8. * using (var formatConverter = new FormatConverter(_imagingFactory)) * { * formatConverter.Initialize(bitmapDecoder.GetFrame(0), PixelFormat.Format32bppPRGBA, BitmapDitherType.None, null, 0, BitmapPaletteType.Custom); * * // Return a API-independent texture. * var description = new TextureDescription * { * Dimension = TextureDimension.Texture2D, * Width = formatConverter.Size.Width, * Height = formatConverter.Size.Height, * Depth = 1, * MipLevels = 1, * ArraySize = 1, * Format = DataFormat.R8G8B8A8_UNorm * }; * * var texture = new Texture(description); * var image = texture.Images[0]; * formatConverter.CopyPixels(image.Data, image.RowPitch); * * return texture; * } * } * //*/ // DirectXTex version: using (var decoder = new BitmapDecoder(imagingFactory, stream, DecodeOptions.CacheOnDemand)) { var frame = decoder.GetFrame(0); // Get metadata. Guid convertGuid; var description = DecodeMetadata(imagingFactory, flags, decoder, frame, out convertGuid); if (description.ArraySize > 1 && (flags & WicFlags.AllFrames) != 0) { return(DecodeMultiframe(imagingFactory, flags, description, decoder)); } return(DecodeSingleframe(imagingFactory, flags, description, convertGuid, frame)); } }
private static BitmapDitherType GetWicDither(WicFlags flags) { if ((flags & WicFlags.Dither) != 0) return BitmapDitherType.Ordered4x4; if ((flags & WicFlags.DitherDiffusion) != 0) return BitmapDitherType.ErrorDiffusion; return BitmapDitherType.None; }
private static BitmapInterpolationMode GetWicInterp(WicFlags flags) { if ((flags & WicFlags.FilterPoint) != 0) return BitmapInterpolationMode.NearestNeighbor; if ((flags & WicFlags.FilterLinear) != 0) return BitmapInterpolationMode.Linear; if ((flags & WicFlags.FilterCubic) != 0) return BitmapInterpolationMode.Cubic; return BitmapInterpolationMode.Fant; }
private static BitmapDitherType GetWicDither(WicFlags flags) { if ((flags & WicFlags.Dither) != 0) { return(BitmapDitherType.Ordered4x4); } if ((flags & WicFlags.DitherDiffusion) != 0) { return(BitmapDitherType.ErrorDiffusion); } return(BitmapDitherType.None); }
private static Texture DecodeSingleframe(ImagingFactory imagingFactory, WicFlags flags, TextureDescription description, Guid convertGuid, BitmapFrameDecode frame) { var texture = new Texture(description); var image = texture.Images[0]; if (convertGuid == Guid.Empty) { frame.CopyPixels(image.Data, image.RowPitch); } else { using (var converter = new FormatConverter(imagingFactory)) { converter.Initialize(frame, convertGuid, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); converter.CopyPixels(image.Data, image.RowPitch); } } return texture; }
private static void EncodeImage(ImagingFactory imagingFactory, Image image, WicFlags flags, Guid containerFormat, BitmapFrameEncode frame) { Guid pfGuid = ToWic(image.Format, false); frame.Initialize(); frame.SetSize(image.Width, image.Height); frame.SetResolution(72, 72); Guid targetGuid = pfGuid; frame.SetPixelFormat(ref targetGuid); EncodeMetadata(frame, containerFormat, image.Format); if (targetGuid != pfGuid) { // Conversion required to write. GCHandle handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); using (var source = new Bitmap(imagingFactory, image.Width, image.Height, pfGuid, new DataRectangle(handle.AddrOfPinnedObject(), image.RowPitch), image.Data.Length)) { using (var converter = new FormatConverter(imagingFactory)) { if (!converter.CanConvert(pfGuid, targetGuid)) { throw new NotSupportedException("Format conversion is not supported."); } converter.Initialize(source, targetGuid, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); frame.WriteSource(converter, new Rectangle(0, 0, image.Width, image.Height)); } } handle.Free(); } else { // No conversion required. frame.WritePixels(image.Height, image.RowPitch, image.Data); } frame.Commit(); }
private static TextureDescription DecodeMetadata(ImagingFactory imagingFactory, WicFlags flags, BitmapDecoder decoder, BitmapFrameDecode frame, out Guid pixelFormat) { var size = frame.Size; var description = new TextureDescription { Dimension = TextureDimension.Texture2D, Width = size.Width, Height = size.Height, Depth = 1, MipLevels = 1, ArraySize = (flags & WicFlags.AllFrames) != 0 ? decoder.FrameCount : 1, Format = DetermineFormat(imagingFactory, frame.PixelFormat, flags, out pixelFormat) }; if (description.Format == DataFormat.Unknown) throw new NotSupportedException("The pixel format is not supported."); if ((flags & WicFlags.IgnoreSrgb) == 0) { // Handle sRGB. #pragma warning disable 168 try { Guid containerFormat = decoder.ContainerFormat; var metareader = frame.MetadataQueryReader; if (metareader != null) { // Check for sRGB color space metadata. bool sRgb = false; if (containerFormat == ContainerFormatGuids.Png) { // Check for sRGB chunk. if (metareader.GetMetadataByName("/sRGB/RenderingIntent") != null) sRgb = true; } else if (containerFormat == ContainerFormatGuids.Jpeg) { if (Equals(metareader.GetMetadataByName("/app1/ifd/exif/{ushort=40961}"), 1)) sRgb = true; } else if (containerFormat == ContainerFormatGuids.Tiff) { if (Equals(metareader.GetMetadataByName("/ifd/exif/{ushort=40961}"), 1)) sRgb = true; } else { if (Equals(metareader.GetMetadataByName("System.Image.ColorSpace"), 1)) sRgb = true; } if (sRgb) description.Format = TextureHelper.MakeSRgb(description.Format); } } // ReSharper disable once EmptyGeneralCatchClause catch (Exception exception) { // Some formats just don't support metadata (BMP, ICO, etc.). } } #pragma warning restore 168 return description; }
/// <summary> /// Saves the specified image. /// </summary> /// <param name="imagingFactory"> /// The factory for creating components for the Windows Imaging Component (WIC). /// </param> /// <param name="image">The image.</param> /// <param name="stream">The stream to write to.</param> /// <param name="flags">Additional options.</param> /// <param name="containerFormat">The container format.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="imagingFactory"/>, <paramref name="image"/> or <paramref name="stream"/> is /// <see langword="null"/>. /// </exception> public static void Save(ImagingFactory imagingFactory, Image image, Stream stream, WicFlags flags, Guid containerFormat) { if (imagingFactory == null) throw new ArgumentNullException("imagingFactory"); if (image == null) throw new ArgumentNullException("image"); if (stream == null) throw new ArgumentNullException("stream"); EncodeSingleframe(imagingFactory, image, stream, containerFormat, flags); }
/// <summary> /// Saves the specified images. /// </summary> /// <param name="imagingFactory"> /// The factory for creating components for the Windows Imaging Component (WIC). /// </param> /// <param name="images">The images.</param> /// <param name="stream">The stream to write to.</param> /// <param name="flags">Additional options.</param> /// <param name="containerFormat"> /// The container format (see <see cref="ContainerFormatGuids"/>). /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="imagingFactory"/>, <paramref name="images"/> or <paramref name="stream"/> is /// <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="images"/> is empty. /// </exception> public static void Save(ImagingFactory imagingFactory, IList<Image> images, Stream stream, WicFlags flags, Guid containerFormat) { if (imagingFactory == null) throw new ArgumentNullException("imagingFactory"); if (images == null) throw new ArgumentNullException("images"); if (images.Count == 0) throw new ArgumentException("The list of images must not be empty.", "images"); if (stream == null) throw new ArgumentNullException("stream"); if (images.Count > 1) EncodeMultiframe(imagingFactory, images, stream, containerFormat, flags); else EncodeSingleframe(imagingFactory, images[0], stream, containerFormat, flags); }
/// <summary> /// Loads the specified image(s). /// </summary> /// <param name="imagingFactory"> /// The factory for creating components for the Windows Imaging Component (WIC). /// </param> /// <param name="stream">The stream to read from.</param> /// <param name="flags">Additional options.</param> /// <returns>A <see cref="Texture"/> representing the image(s).</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="imagingFactory"/>, <paramref name="stream"/> is <see langword="null"/>. /// </exception> public static Texture Load(ImagingFactory imagingFactory, Stream stream, WicFlags flags) { if (imagingFactory == null) throw new ArgumentNullException("imagingFactory"); if (stream == null) throw new ArgumentNullException("stream"); // Simple version: /* using (var bitmapDecoder = new BitmapDecoder(_imagingFactory, stream, DecodeOptions.CacheOnDemand)) { // Convert the image to pre-multiplied RGBA8. using (var formatConverter = new FormatConverter(_imagingFactory)) { formatConverter.Initialize(bitmapDecoder.GetFrame(0), PixelFormat.Format32bppPRGBA, BitmapDitherType.None, null, 0, BitmapPaletteType.Custom); // Return a API-independent texture. var description = new TextureDescription { Dimension = TextureDimension.Texture2D, Width = formatConverter.Size.Width, Height = formatConverter.Size.Height, Depth = 1, MipLevels = 1, ArraySize = 1, Format = DataFormat.R8G8B8A8_UNorm }; var texture = new Texture(description); var image = texture.Images[0]; formatConverter.CopyPixels(image.Data, image.RowPitch); return texture; } } //*/ // DirectXTex version: using (var decoder = new BitmapDecoder(imagingFactory, stream, DecodeOptions.CacheOnDemand)) { var frame = decoder.GetFrame(0); // Get metadata. Guid convertGuid; var description = DecodeMetadata(imagingFactory, flags, decoder, frame, out convertGuid); if (description.ArraySize > 1 && (flags & WicFlags.AllFrames) != 0) return DecodeMultiframe(imagingFactory, flags, description, decoder); return DecodeSingleframe(imagingFactory, flags, description, convertGuid, frame); } }
private static void EncodeMultiframe(ImagingFactory imagingFactory, IList<Image> images, Stream stream, Guid containerFormat, WicFlags flags) { using (var encoder = new BitmapEncoder(imagingFactory, containerFormat)) { using (var encoderInfo = encoder.EncoderInfo) { if (!encoderInfo.IsMultiframeSupported) throw new NotSupportedException("The specified image format does not support multiple frames."); } encoder.Initialize(stream); for (int i = 0; i < images.Count; i++) { var image = images[i]; using (var frame = new BitmapFrameEncode(encoder)) EncodeImage(imagingFactory, image, flags, containerFormat, frame); } encoder.Commit(); } }
// Returns the DXGI format and optionally the WIC pixel GUID to convert to. private static DataFormat DetermineFormat(ImagingFactory imagingFactory, Guid pixelFormat, WicFlags flags, out Guid convertGuid) { DataFormat format = ToFormat(pixelFormat); convertGuid = Guid.Empty; if (format == DataFormat.Unknown) { for (int i = 0; i < WICConvertTable.Length; ++i) { if (pixelFormat == WICConvertTable[i].Source) { convertGuid = WICConvertTable[i].Target; format = ToFormat(WICConvertTable[i].Target); Debug.Assert(format != DataFormat.Unknown); break; } } } // Handle special cases based on flags switch (format) { case DataFormat.B8G8R8A8_UNORM: // BGRA case DataFormat.B8G8R8X8_UNORM: // BGRX if ((flags & WicFlags.ForceRgb) != 0) { format = DataFormat.R8G8B8A8_UNORM; convertGuid = PixelFormat.Format32bppRGBA; } break; case DataFormat.R10G10B10_XR_BIAS_A2_UNORM: if ((flags & WicFlags.NoX2Bias) != 0) { format = DataFormat.R10G10B10A2_UNORM; convertGuid = PixelFormat.Format32bppRGBA1010102; } break; case DataFormat.B5G5R5A1_UNORM: case DataFormat.B5G6R5_UNORM: if ((flags & WicFlags.No16Bpp) != 0) { format = DataFormat.R8G8B8A8_UNORM; convertGuid = PixelFormat.Format32bppRGBA; } break; case DataFormat.R1_UNORM: if ((flags & WicFlags.FlagsAllowMono) == 0) { // By default we want to promote a black & white to grayscale since R1 is not a generally supported D3D format format = DataFormat.R8_UNORM; convertGuid = PixelFormat.Format8bppGray; } break; } return format; }
private static void EncodeSingleframe(ImagingFactory imagingFactory, Image image, Stream stream, Guid containerFormat, WicFlags flags) { using (var encoder = new BitmapEncoder(imagingFactory, containerFormat, stream)) { using (var frame = new BitmapFrameEncode(encoder)) { if (containerFormat == ContainerFormatGuids.Bmp) { #pragma warning disable 168 // ReSharper disable once EmptyGeneralCatchClause try { frame.Options.Set("EnableV5Header32bppBGRA", true); } catch (Exception exception) { // WIC2 is available on Windows 8 and Windows 7 SP1 with KB 2670838 installed // SharpDX: ImagingFactory2 is only available in Windows 8 build. } #pragma warning restore 168 } EncodeImage(imagingFactory, image, flags, containerFormat, frame); encoder.Commit(); } } }
private static void EncodeImage(ImagingFactory imagingFactory, Image image, WicFlags flags, Guid containerFormat, BitmapFrameEncode frame) { Guid pfGuid = ToWic(image.Format, false); frame.Initialize(); frame.SetSize(image.Width, image.Height); frame.SetResolution(72, 72); Guid targetGuid = pfGuid; frame.SetPixelFormat(ref targetGuid); EncodeMetadata(frame, containerFormat, image.Format); if (targetGuid != pfGuid) { // Conversion required to write. GCHandle handle = GCHandle.Alloc(image.Data, GCHandleType.Pinned); using (var source = new Bitmap(imagingFactory, image.Width, image.Height, pfGuid, new DataRectangle(handle.AddrOfPinnedObject(), image.RowPitch), image.Data.Length)) { using (var converter = new FormatConverter(imagingFactory)) { if (!converter.CanConvert(pfGuid, targetGuid)) throw new NotSupportedException("Format conversion is not supported."); converter.Initialize(source, targetGuid, GetWicDither(flags), null, 0, BitmapPaletteType.Custom); frame.WriteSource(converter, new Rectangle(0, 0, image.Width, image.Height)); } } handle.Free(); } else { // No conversion required. frame.WritePixels(image.Height, image.RowPitch, image.Data); } frame.Commit(); }