/// <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 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 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 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); }