/// <summary> /// Function to retrieve palette information for indexed images. /// </summary> /// <param name="wic">The WIC interface.</param> /// <param name="bitmap">The bitmap to derive the palette from (only used when encoding).</param> /// <returns> /// A tuple containing the palette data, alpha percentage and the type of palette. /// </returns> internal override Tuple <Palette, double, BitmapPaletteType> GetPaletteInfo(GorgonWICImage wic, Bitmap bitmap) { Palette palette; if (Palette.Count == 0) { // If decoding, just return the default, otherwise we'll need to generate from the frame. if (bitmap == null) { return(base.GetPaletteInfo(wic, null)); } palette = new Palette(wic.Factory); palette.Initialize(bitmap, 256, true); return(new Tuple <Palette, double, BitmapPaletteType>(palette, AlphaThresholdPercent, BitmapPaletteType.Custom)); } // Generate from our custom palette. var paletteColors = new DX.Color4[256]; int size = paletteColors.Length.Min(Palette.Count); for (int i = 0; i < size; i++) { paletteColors[i] = Palette[i].SharpDXColor4; } palette = new Palette(wic.Factory); palette.Initialize(paletteColors); return(new Tuple <Palette, double, BitmapPaletteType>(palette, AlphaThresholdPercent, BitmapPaletteType.Custom)); }
/// <summary> /// Function to create 2D Gorgon image data from a single System.Drawing.Image. /// </summary> /// <param name="wic">Windows Imaging Component interface to use.</param> /// <param name="image">An image to convert.</param> /// <param name="options">Options for conversion.</param> /// <returns>The converted image data.</returns> public static GorgonImageData Create2DImageDataFromImage(GorgonWICImage wic, Image image, GorgonGDIOptions options) { if (options.Format == BufferFormat.Unknown) { options.Format = GetBufferFormat(image.PixelFormat); } if (options.Format == BufferFormat.Unknown) { throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, image.PixelFormat)); } if (options.Width < 1) { options.Width = image.Width; } if (options.Height < 1) { options.Height = image.Height; } // Specify 0 to generate a full mip chain. options.MipCount = options.MipCount < 1 ? GorgonImageData.GetMaxMipCount(options.Width, options.Height) : options.MipCount.Min(GorgonImageData.GetMaxMipCount(options.Width, options.Height)); // Create our settings. var settings = new GorgonTexture2DSettings { Width = options.Width, Height = options.Height, MipCount = options.MipCount, ArrayCount = 1, Format = options.Format, AllowUnorderedAccessViews = options.AllowUnorderedAccess, ShaderViewFormat = options.ViewFormat, Usage = options.Usage }; // Create our image data. var data = new GorgonImageData(settings); // Using the image, convert to a WIC bitmap object. using (Bitmap bitmap = wic.CreateWICImageFromImage(image)) { for (int mipLevel = 0; mipLevel < options.MipCount; mipLevel++) { var buffer = data.Buffers[mipLevel]; wic.AddWICBitmapToImageData(bitmap, options.Filter, options.Dither, buffer, options.UseClipping); } } return(data); }
/// <summary> /// Function to retrieve meta data from the image. /// </summary> /// <param name="wic">WIC interface.</param> /// <param name="decoder">Decoder for the image.</param> /// <param name="frame">Frame in the image to decode.</param> /// <param name="bestFormatMatch">The best match for the pixel format.</param> /// <returns>Settings for the new image.</returns> internal IImageSettings ReadMetaData(GorgonWICImage wic, BitmapDecoder decoder, BitmapFrameDecode frame, out Guid bestFormatMatch) { return(new GorgonTexture2DSettings { Width = frame.Size.Width, Height = frame.Size.Height, MipCount = 1, ArrayCount = (CodecUseAllFrames && SupportsMultipleFrames) ? decoder.FrameCount : 1, Format = wic.FindBestFormat(frame.PixelFormat, DecodeFlags, out bestFormatMatch) }); }
/// <summary> /// Function to read the data from a frame. /// </summary> /// <param name="wic">WIC interface.</param> /// <param name="data">Image data to populate.</param> /// <param name="srcFormat">Source image format.</param> /// <param name="convertFormat">Conversion format.</param> /// <param name="frame">Frame containing the image data.</param> private void ReadFrame(GorgonWICImage wic, GorgonImageData data, Guid srcFormat, Guid convertFormat, BitmapFrameDecode frame) { var buffer = data.Buffers[0]; // We don't need to convert, so just leave. if ((convertFormat == Guid.Empty) || (srcFormat == convertFormat)) { frame.CopyPixels(buffer.PitchInformation.RowPitch, buffer.Data.BasePointer, buffer.PitchInformation.SlicePitch); return; } // Perform conversion. using (var converter = new FormatConverter(wic.Factory)) { bool isIndexed = ((frame.PixelFormat == PixelFormat.Format8bppIndexed) || (frame.PixelFormat == PixelFormat.Format4bppIndexed) || (frame.PixelFormat == PixelFormat.Format2bppIndexed) || (frame.PixelFormat == PixelFormat.Format1bppIndexed)); Tuple <Palette, double, BitmapPaletteType> paletteInfo = null; try { // If the pixel format is indexed, then retrieve a palette. if (isIndexed) { paletteInfo = GetPaletteInfo(wic, null); } // If we've defined a palette for an indexed image, then copy it to a bitmap and set its palette. if ((paletteInfo != null) && (paletteInfo.Item1 != null)) { using (var tempBitmap = new Bitmap(wic.Factory, frame, BitmapCreateCacheOption.CacheOnDemand)) { tempBitmap.Palette = paletteInfo.Item1; converter.Initialize(tempBitmap, convertFormat, (BitmapDitherType)Dithering, paletteInfo.Item1, paletteInfo.Item2, paletteInfo.Item3); converter.CopyPixels(buffer.PitchInformation.RowPitch, buffer.Data.BasePointer, buffer.PitchInformation.SlicePitch); } return; } // Only apply palettes to indexed image data. converter.Initialize(frame, convertFormat, (BitmapDitherType)Dithering, null, 0.0, BitmapPaletteType.Custom); converter.CopyPixels(buffer.PitchInformation.RowPitch, buffer.Data.BasePointer, buffer.PitchInformation.SlicePitch); } finally { if ((paletteInfo != null) && (paletteInfo.Item1 != null)) { paletteInfo.Item1.Dispose(); } } } }
/// <summary> /// Function to create an array of System.Drawing.Images from an image data object. /// </summary> /// <param name="imageData">Image data to process.</param> /// <returns>A list of GDI+ images.</returns> public static Image[] CreateGDIImagesFromImageData(GorgonImageData imageData) { PixelFormat?format = GetPixelFormat(imageData.Settings.Format); Bitmap[] bitmaps = null; Image[] images; if (format == null) { format = GetPixelFormat(imageData.Settings.Format); if (format == null) { throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, imageData.Settings.Format)); } } using (var wic = new GorgonWICImage()) { try { bitmaps = wic.CreateWICBitmapsFromImageData(imageData); images = new Image[bitmaps.Length]; for (int i = 0; i < bitmaps.Length; i++) { images[i] = wic.CreateGDIImageFromWICBitmap(bitmaps[i], format.Value); } } finally { // Clean up. if (bitmaps != null) { foreach (var bitmap in bitmaps) { bitmap.Dispose(); } } } } return(images); }
/// <summary> /// Function to perform any post processing on image data. /// </summary> internal void PostProcess(GorgonImageData data) { IImageSettings destSettings = data.Settings.Clone(); int width = Width > 0 ? Width : data.Settings.Width; int height = Height > 0 ? Height : data.Settings.Height; int mipCount = MipCount > 0 ? MipCount : data.Settings.MipCount; BufferFormat format = (Format != BufferFormat.Unknown) ? Format : data.Settings.Format; Rectangle newSize = Rectangle.Empty; int mipStart = 0; var sourceInfo = GorgonBufferFormatInfo.GetInfo(data.Settings.Format); var destInfo = GorgonBufferFormatInfo.GetInfo(format); // First, confirm whether we can perform format conversions. using (var wic = new GorgonWICImage()) { Guid srcPixelFormat = wic.GetGUID(data.Settings.Format); Guid destPixelFormat = wic.GetGUID(format); // Do nothing if we can't do anything with the source format. if (srcPixelFormat == Guid.Empty) { return; } // Cancel conversion if we're using the same format. if (srcPixelFormat == destPixelFormat) { destPixelFormat = Guid.Empty; } // Get the new size. if ((width != data.Settings.Width) || (height != data.Settings.Height)) { newSize = new Rectangle(0, 0, width, height); } // Set up destination buffer settings. destSettings.Format = format; destSettings.Width = width; destSettings.Height = height; destSettings.MipCount = mipCount; // Ensure we don't go over the maximum. int maxMips = GorgonImageData.GetMaxMipCount(destSettings); if (mipCount > maxMips) { mipCount = maxMips; } // Nothing's going to happen here, so leave. if ((destPixelFormat == Guid.Empty) && (newSize == Rectangle.Empty) && (mipCount == 1)) { return; } // Create our worker buffer. GorgonImageData destData; using (destData = new GorgonImageData(destSettings)) { // The first step is to convert and resize our images: if ((destPixelFormat != Guid.Empty) || (newSize != Rectangle.Empty)) { for (int array = 0; array < destSettings.ArrayCount; array++) { // We're not going to copy mip levels at this point, that will come in the next step. for (int depth = 0; depth < destSettings.Depth; depth++) { // Get our source/destination buffers. var sourceBuffer = data.Buffers[0, data.Settings.ImageType == ImageType.Image3D ? depth : array]; var destBuffer = destData.Buffers[0, data.Settings.ImageType == ImageType.Image3D ? depth : array]; var dataRect = new DataRectangle(sourceBuffer.Data.BasePointer, sourceBuffer.PitchInformation.RowPitch); // Create a temporary WIC bitmap to work with. using (var bitmap = new Bitmap(wic.Factory, sourceBuffer.Width, sourceBuffer.Height, srcPixelFormat, dataRect, sourceBuffer.PitchInformation.SlicePitch)) { wic.TransformImageData(bitmap, destBuffer.Data.BasePointer, destBuffer.PitchInformation.RowPitch, destBuffer.PitchInformation.SlicePitch, destPixelFormat, sourceInfo.IssRGB, destInfo.IssRGB, Dithering, newSize, Clip, Filter); } } } // Adjust the mip map starting point. mipStart = 1; } // Next, we need to build mip maps. if (destSettings.MipCount > 1) { // The first step is to convert and resize our images: for (int array = 0; array < destSettings.ArrayCount; array++) { int mipDepth = destSettings.Depth; for (int mip = mipStart; mip < destSettings.MipCount; mip++) { // We're not going to copy mip levels at this point, that will come in the next step. for (int depth = 0; depth < mipDepth; depth++) { // Get our source/destination buffers. var sourceBuffer = destData.Buffers[0, data.Settings.ImageType == ImageType.Image3D ? (destSettings.Depth / mipDepth) * depth : array]; var destBuffer = destData.Buffers[mip, data.Settings.ImageType == ImageType.Image3D ? depth : array]; var dataRect = new DataRectangle(sourceBuffer.Data.BasePointer, sourceBuffer.PitchInformation.RowPitch); // Create a temporary WIC bitmap to work with. using (var bitmap = new Bitmap(wic.Factory, sourceBuffer.Width, sourceBuffer.Height, srcPixelFormat, dataRect, sourceBuffer.PitchInformation.SlicePitch)) { wic.TransformImageData(bitmap, destBuffer.Data.BasePointer, destBuffer.PitchInformation.RowPitch, destBuffer.PitchInformation.SlicePitch, Guid.Empty, false, false, ImageDithering.None, new Rectangle(0, 0, destBuffer.Width, destBuffer.Height), false, Filter); } } if (mipDepth > 1) { mipDepth >>= 1; } } } } // Update our data. data.TakeOwnership(destData); } } }
/// <summary> /// Function to create a 3D Gorgon image data from multiple System.Drawing.Images. /// </summary> /// <param name="wic">Windows Imaging Component interface to use.</param> /// <param name="images">Images to convert.</param> /// <param name="options">Conversion options.</param> /// <returns>The converted image data.</returns> public static GorgonImageData Create3DImageDataFromImages(GorgonWICImage wic, IList <Image> images, GorgonGDIOptions options) { if (options.Format == BufferFormat.Unknown) { options.Format = GetBufferFormat(images[0].PixelFormat); } if (options.Format == BufferFormat.Unknown) { throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, images[0].PixelFormat)); } if (images.Any(item => item.PixelFormat != images[0].PixelFormat)) { throw new GorgonException(GorgonResult.CannotCreate, string.Format(Resources.GORGFX_IMAGE_MUST_BE_SAME_FORMAT, images[0].PixelFormat)); } if (options.Width <= 0) { options.Width = images[0].Width; } if (options.Height <= 0) { options.Height = images[0].Height; } if (options.Depth < 1) { options.Depth = 1; } options.MipCount = options.MipCount < 1 ? 1 : options.MipCount.Min(GorgonImageData.GetMaxMipCount(options.Width, options.Height, options.Depth)); // Set the depth to the number of images if there are no mip-maps. if ((images.Count > 1) && (options.MipCount == 1)) { options.Depth = images.Count; } // Create our settings. var settings = new GorgonTexture3DSettings { Width = options.Width, Height = options.Height, Depth = options.Depth, MipCount = options.MipCount, Format = options.Format, AllowUnorderedAccessViews = options.AllowUnorderedAccess, ShaderViewFormat = options.ViewFormat, Usage = options.Usage }; // Only volume textures that are size to a power of 2 can have mip maps. if ((!settings.IsPowerOfTwo) && (options.MipCount > 1)) { throw new GorgonException(GorgonResult.CannotCreate, Resources.GORGFX_IMAGE_VOLUME_NOT_POWER_OF_TWO); } // Create our image data. var data = new GorgonImageData(settings); int depthMip = options.Depth; int imageIndex = 0; for (int mipLevel = 0; mipLevel < data.Settings.MipCount; mipLevel++) { if (imageIndex >= images.Count) { data.Dispose(); throw new GorgonException(GorgonResult.CannotCreate, Resources.GORGFX_IMAGE_VOLUME_MIPCOUNT_DEPTHCOUNT_TOO_LARGE); } for (int depth = 0; depth < depthMip; depth++) { var image = images[imageIndex + depth]; // Skip NULL images. if (image == null) { continue; } // Using the image, convert to a WIC bitmap object. using (var bitmap = wic.CreateWICImageFromImage(image)) { var buffer = data.Buffers[mipLevel, depth]; wic.AddWICBitmapToImageData(bitmap, options.Filter, options.Dither, buffer, options.UseClipping); } } imageIndex += depthMip; // Decrease depth based on mip level. if (depthMip > 1) { depthMip >>= 1; } } return(data); }
/// <summary> /// Function to create 2D Gorgon image data from multiple single System.Drawing.Images. /// </summary> /// <param name="wic">Windows Imaging Component interface to use.</param> /// <param name="images">Images to convert.</param> /// <param name="options">Options for conversion.</param> /// <returns>The converted image data.</returns> public static GorgonImageData Create2DImageDataFromImages(GorgonWICImage wic, IList <Image> images, GorgonGDIOptions options) { if (options.Format == BufferFormat.Unknown) { options.Format = GetBufferFormat(images[0].PixelFormat); } if (options.Format == BufferFormat.Unknown) { throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, images[0].PixelFormat)); } if (images.Any(item => item.PixelFormat != images[0].PixelFormat)) { throw new GorgonException(GorgonResult.CannotCreate, string.Format(Resources.GORGFX_IMAGE_MUST_BE_SAME_FORMAT, images[0].PixelFormat)); } if (options.Width < 1) { options.Width = images[0].Width; } if (options.Height < 1) { options.Height = images[0].Height; } if (options.ArrayCount < 1) { options.ArrayCount = 1; } options.MipCount = options.MipCount < 1 ? 1 : options.MipCount.Min(GorgonImageData.GetMaxMipCount(options.Width, options.Height)); // Create our settings. var settings = new GorgonTexture2DSettings { Width = options.Width, Height = options.Height, MipCount = options.MipCount, ArrayCount = options.ArrayCount, Format = options.Format, AllowUnorderedAccessViews = options.AllowUnorderedAccess, ShaderViewFormat = options.ViewFormat, Usage = options.Usage }; if ((options.ArrayCount * options.MipCount) > images.Count) { throw new GorgonException(GorgonResult.CannotCreate, Resources.GORGFX_IMAGE_MIPCOUNT_ARRAYCOUNT_TOO_LARGE); } // Create our image data. var data = new GorgonImageData(settings); for (int array = 0; array < data.Settings.ArrayCount; array++) { for (int mipLevel = 0; mipLevel < data.Settings.MipCount; mipLevel++) { var image = images[array * data.Settings.MipCount + mipLevel]; if (image == null) { continue; } // Using the image, convert to a WIC bitmap object. using (var bitmap = wic.CreateWICImageFromImage(image)) { var buffer = data.Buffers[mipLevel, array]; wic.AddWICBitmapToImageData(bitmap, options.Filter, options.Dither, buffer, options.UseClipping); } } } return(data); }
/// <summary> /// Function to determine if this codec can read the file or not. /// </summary> /// <param name="stream">Stream used to read the file information.</param> /// <returns> /// TRUE if the codec can read the file, FALSE if not. /// </returns> /// <exception cref="System.ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is NULL (Nothing in VB.Net).</exception> /// <exception cref="System.IO.IOException">Thrown when the stream is write-only. /// <para>-or-</para> /// <para>Thrown when the stream cannot perform seek operations.</para> /// </exception> public override bool IsReadable(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new IOException(Resources.GORGFX_STREAM_READ_ONLY); } if (!stream.CanSeek) { throw new IOException(Resources.GORGFX_STREAM_EOF); } long position = stream.Position; try { // Get our WIC interface. // Wrap the stream so WIC doesn't mess up the position. var wrapperStream = new GorgonStreamWrapper(stream); using (var wic = new GorgonWICImage()) { using (var decoder = new BitmapDecoder(wic.Factory, SupportedFormat)) { using (var wicStream = new WICStream(wic.Factory, wrapperStream)) { try { decoder.Initialize(wicStream, DecodeOptions.CacheOnDemand); } catch (SharpDXException) { return(false); } // Only load supported WIC formats. if (SupportedFormat != decoder.ContainerFormat) { return(false); } using (var frame = decoder.GetFrame(0)) { Guid bestFormat; var settings = ReadMetaData(wic, decoder, frame, out bestFormat); return(settings.Format != BufferFormat.Unknown); } } } } } finally { stream.Position = position; } }
/// <summary> /// Function to read file meta data. /// </summary> /// <param name="stream">Stream used to read the metadata.</param> /// <returns> /// The image meta data as a <see cref="GorgonLibrary.Graphics.IImageSettings">IImageSettings</see> value. /// </returns> /// <exception cref="System.ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is NULL (Nothing in VB.Net).</exception> /// <exception cref="System.IO.IOException">Thrown when the stream is write-only. /// <para>-or-</para> /// <para>Thrown when the stream cannot perform seek operations.</para> /// </exception> /// <exception cref="System.IO.EndOfStreamException">Thrown when an attempt to read beyond the end of the stream is made.</exception> public override IImageSettings GetMetaData(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new IOException(Resources.GORGFX_STREAM_WRITE_ONLY); } if (!stream.CanSeek) { throw new IOException(Resources.GORGFX_STREAM_NO_SEEK); } long position = stream.Position; try { // Wrap the stream so WIC doesn't mess up the position. var wrapperStream = new GorgonStreamWrapper(stream); // Get our WIC interface. using (var wic = new GorgonWICImage()) { using (var decoder = new BitmapDecoder(wic.Factory, SupportedFormat)) { using (var wicStream = new WICStream(wic.Factory, wrapperStream)) { try { decoder.Initialize(wicStream, DecodeOptions.CacheOnDemand); } catch (SharpDXException) { throw new IOException(string.Format(Resources.GORGFX_IMAGE_FILE_INCORRECT_ENCODER, Codec)); } using (var frame = decoder.GetFrame(0)) { Guid bestFormat; var settings = ReadMetaData(wic, decoder, frame, out bestFormat); if (settings.Format == BufferFormat.Unknown) { throw new IOException(string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, settings.Format)); } return(settings); } } } } } finally { stream.Position = position; } }
/// <summary> /// Function to persist image data to a stream. /// </summary> /// <param name="imageData"><see cref="GorgonLibrary.Graphics.GorgonImageData">Gorgon image data</see> to persist.</param> /// <param name="stream">Stream that will contain the data.</param> protected internal override void SaveToStream(GorgonImageData imageData, Stream stream) { int frameCount = 1; // Wrap the stream so WIC doesn't mess up the position. using (var wrapperStream = new GorgonStreamWrapper(stream)) { using (var wic = new GorgonWICImage()) { // Find a compatible format. Guid targetFormat = wic.GetGUID(imageData.Settings.Format); if (targetFormat == Guid.Empty) { throw new IOException(string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, imageData.Settings.Format)); } Guid actualFormat = targetFormat; using (var encoder = new BitmapEncoder(wic.Factory, SupportedFormat)) { try { encoder.Initialize(wrapperStream); AddCustomMetaData(encoder, null, 0, imageData.Settings, null); } catch (SharpDXException) { // Repackage this exception to keep in line with our API. throw new IOException(string.Format(Resources.GORGFX_IMAGE_FILE_INCORRECT_ENCODER, Codec)); } using (var encoderInfo = encoder.EncoderInfo) { if ((imageData.Settings.ArrayCount > 1) && (CodecUseAllFrames) && (encoderInfo.IsMultiframeSupported)) { frameCount = imageData.Settings.ArrayCount; } for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { using (var frame = new BitmapFrameEncode(encoder)) { var buffer = imageData.Buffers[0, frameIndex]; frame.Initialize(); frame.SetSize(buffer.Width, buffer.Height); frame.SetResolution(72, 72); frame.SetPixelFormat(ref actualFormat); SetFrameOptions(frame); // If the image encoder doesn't like the format we've chosen, then we'll need to convert to // the best format for the codec. if (targetFormat != actualFormat) { var rect = new DataRectangle(buffer.Data.BasePointer, buffer.PitchInformation.RowPitch); using (var bitmap = new Bitmap(wic.Factory, buffer.Width, buffer.Height, targetFormat, rect)) { // If we're using a codec that supports 8 bit indexed data, then get the palette info. var paletteInfo = GetPaletteInfo(wic, bitmap); if (paletteInfo == null) { throw new IOException(string.Format(Resources.GORGFX_IMAGE_FILE_INCORRECT_ENCODER, Codec)); } try { using (var converter = new FormatConverter(wic.Factory)) { converter.Initialize(bitmap, actualFormat, (BitmapDitherType)Dithering, paletteInfo.Item1, paletteInfo.Item2, paletteInfo.Item3); if (paletteInfo.Item1 != null) { frame.Palette = paletteInfo.Item1; } AddCustomMetaData(encoder, frame, frameIndex, imageData.Settings, (paletteInfo.Item1 != null) ? paletteInfo.Item1.Colors : null); frame.WriteSource(converter); } } finally { if (paletteInfo.Item1 != null) { paletteInfo.Item1.Dispose(); } } } } else { // No conversion was needed, just dump as-is. AddCustomMetaData(encoder, frame, frameIndex, imageData.Settings, null); frame.WritePixels(buffer.Height, buffer.Data.BasePointer, buffer.PitchInformation.RowPitch, buffer.PitchInformation.SlicePitch); } frame.Commit(); } } } encoder.Commit(); } } } }
/// <summary> /// Function to load an image from a stream. /// </summary> /// <param name="stream">Stream containing the data to load.</param> /// <param name="size">Size of the data to read, in bytes.</param> /// <returns> /// The image data that was in the stream. /// </returns> protected internal override GorgonImageData LoadFromStream(GorgonDataStream stream, int size) { GorgonImageData result = null; // Get our WIC interface. var wrapperStream = new GorgonStreamWrapper(stream); using (var wic = new GorgonWICImage()) { using (var decoder = new BitmapDecoder(wic.Factory, SupportedFormat)) { using (var wicStream = new WICStream(wic.Factory, wrapperStream)) { try { decoder.Initialize(wicStream, DecodeOptions.CacheOnDemand); } catch (SharpDXException) { // Repackage this exception to keep in line with our API. throw new IOException(string.Format(Resources.GORGFX_IMAGE_FILE_INCORRECT_DECODER, Codec)); } using (var frame = decoder.GetFrame(0)) { Guid bestFormat; var settings = ReadMetaData(wic, decoder, frame, out bestFormat); if (settings.Format == BufferFormat.Unknown) { throw new IOException(string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, settings.Format)); } // Create our image data. try { _actualArrayCount = settings.ArrayCount; if (ArrayCount > 0) { settings.ArrayCount = ArrayCount; } result = new GorgonImageData(settings); if ((settings.ArrayCount > 1) && (_actualArrayCount > 1)) { ReadFrames(wic, result, decoder); } else { ReadFrame(wic, result, frame.PixelFormat, bestFormat, frame); } // If we've not read the full length of the data (WIC seems to ignore the CRC on the IEND chunk for PNG files for example), // then we need to move the pointer up by however many bytes we've missed. if (wrapperStream.Position < size) { wrapperStream.Position = size; } } catch { // If we run into a problem, dump the memory buffer. if (result != null) { result.Dispose(); } throw; } } } } } return(result); }
/// <summary> /// Function to retrieve palette information for indexed images. /// </summary> /// <param name="wic">The WIC interface.</param> /// <param name="bitmap">The bitmap to derive the palette from (only used when encoding).</param> /// <returns>A tuple containing the palette data, alpha percentage and the type of palette. NULL if we're encoding and we want to generate the palette from the frame.</returns> internal virtual Tuple <Palette, double, BitmapPaletteType> GetPaletteInfo(GorgonWICImage wic, Bitmap bitmap) { return(new Tuple <Palette, double, BitmapPaletteType>(null, 0, BitmapPaletteType.Custom)); }
/// <summary> /// Function to read multiple frames from a decoder that supports multiple frames. /// </summary> /// <param name="wic">WIC interface.</param> /// <param name="data">Image data to populate.</param> /// <param name="decoder">Decoder for the image.</param> private void ReadFrames(GorgonWICImage wic, GorgonImageData data, BitmapDecoder decoder) { Guid bestPixelFormat = wic.GetGUID(data.Settings.Format); // Find the best fit pixel format. if (bestPixelFormat == Guid.Empty) { throw new IOException(string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, data.Settings.Format)); } // Use the image array as the frames. int arrayCount = _actualArrayCount.Min(data.Settings.ArrayCount); for (int array = 0; array < arrayCount; array++) { var buffer = data.Buffers[0, array]; // Get the frame data. using (var frame = decoder.GetFrame(array)) { IntPtr bufferPointer = buffer.Data.BasePointer; Guid frameFormat = frame.PixelFormat; int frameWidth = frame.Size.Width; int frameHeight = frame.Size.Height; var frameOffset = GetFrameOffset(frame); // Calculate the pointer offset if we have an offset from the frame. Only offset if we're clipping the image though. if (((frameOffset.Y != 0) || (frameOffset.X != 0)) && (Clip)) { bufferPointer = buffer.Data.BasePointer + (frameOffset.Y * buffer.PitchInformation.RowPitch) + (frameOffset.X * (buffer.PitchInformation.RowPitch / buffer.Width)); } // Confirm that we actually need to perform clipping. bool needsSizeAdjust = (frameWidth + frameOffset.X > data.Settings.Width) || (frameHeight + frameOffset.Y > data.Settings.Height); // If the formats match, then we don't need to do conversion. if (bestPixelFormat == frameFormat) { // If the width and height are the same then we can just do a straight copy into the buffer. if (((frameWidth == data.Settings.Width) && (frameHeight == data.Settings.Height)) || ((!needsSizeAdjust) && (Clip))) { frame.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); continue; } // We need to scale the image up/down to the size of our image data. if (!Clip) { using (var scaler = new BitmapScaler(wic.Factory)) { scaler.Initialize(frame, data.Settings.Width, data.Settings.Height, (BitmapInterpolationMode)Filter); scaler.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } continue; } using (var clipper = new BitmapClipper(wic.Factory)) { clipper.Initialize(frame, new Rectangle(0, 0, data.Settings.Width, data.Settings.Height)); clipper.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } continue; } // Poop. We need to convert this image. using (var converter = new FormatConverter(wic.Factory)) { converter.Initialize(frame, bestPixelFormat, (BitmapDitherType)Dithering, null, 0.0, BitmapPaletteType.Custom); if (((frameWidth == data.Settings.Width) && (frameHeight == data.Settings.Height)) || ((!needsSizeAdjust) && (Clip))) { converter.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); continue; } // And we need to scale the image. if (!Clip) { using (var scaler = new BitmapScaler(wic.Factory)) { scaler.Initialize(converter, data.Settings.Width, data.Settings.Height, (BitmapInterpolationMode)Filter); scaler.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } continue; } using (var clipper = new BitmapClipper(wic.Factory)) { clipper.Initialize(frame, new Rectangle(0, 0, data.Settings.Width, data.Settings.Height)); clipper.CopyPixels(buffer.PitchInformation.RowPitch, bufferPointer, buffer.PitchInformation.SlicePitch); } } } } }
/// <summary> /// Function to retrieve a list of frame delays for each frame in an animated GIF. /// </summary> /// <param name="stream">Stream containing the animated GIF.</param> /// <returns> /// An array of frame delays (1/100th of a second), or an empty array if the image is not an animated GIF. /// </returns> /// <exception cref="System.ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is NULL (Nothing in VB.Net).</exception> /// <exception cref="System.IO.IOException">Thrown when the stream parameter is write-only. /// <para>-or-</para> /// <para>The data in the stream could not be decoded as GIF file.</para> /// <para>-or-</para> /// <para>The stream cannot perform seek operations.</para> /// </exception> /// <exception cref="System.IO.EndOfStreamException">Thrown when an attempt to read beyond the end of the stream is made.</exception> public ushort[] GetFrameDelays(Stream stream) { var result = new ushort[0]; if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new IOException(Resources.GORGFX_STREAM_WRITE_ONLY); } if (!stream.CanSeek) { throw new IOException(Resources.GORGFX_STREAM_NO_SEEK); } if (!UseAllFrames) { return(result); } long position = stream.Position; try { var wrapperStream = new GorgonStreamWrapper(stream); // Get our WIC interface. using (var wic = new GorgonWICImage()) { using (var decoder = new BitmapDecoder(wic.Factory, SupportedFormat)) { using (var wicStream = new WICStream(wic.Factory, wrapperStream)) { try { decoder.Initialize(wicStream, DecodeOptions.CacheOnDemand); } catch (DX.SharpDXException) { // Repackage the exception to keep in line with our API defintion. throw new IOException(string.Format(Resources.GORGFX_IMAGE_FILE_INCORRECT_DECODER, Codec)); } if (decoder.FrameCount < 2) { return(result); } result = new ushort[decoder.FrameCount]; for (int frame = 0; frame < result.Length; frame++) { using (var frameImage = decoder.GetFrame(frame)) { // Check to see if we can actually read this thing. if (frame == 0) { Guid temp; IImageSettings settings = ReadMetaData(wic, decoder, frameImage, out temp); if (settings.Format == BufferFormat.Unknown) { throw new IOException(string.Format(Resources.GORGFX_FORMAT_NOT_SUPPORTED, settings.Format)); } } using (var reader = frameImage.MetadataQueryReader) { var metaData = reader.GetMetadataByName("/grctlext/Delay"); if (metaData != null) { result[frame] = (ushort)metaData; } else { result[frame] = 0; } } } } } } } } finally { stream.Position = position; } return(result); }