/// <summary>
        /// Function to determine if this codec can read the image data within the stream or not.
        /// </summary>
        /// <param name="stream">The stream that is used to read the image data.</param>
        /// <returns><b>true</b> if the codec can read the file, <b>false</b> if not.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is <b>null</b>.</exception>
        /// <exception cref="IOException">Thrown when the <paramref name="stream"/> is write-only or if the stream cannot perform seek operations.</exception>
        /// <remarks>
        /// <para>
        /// When overloading this method, the implementor should remember to reset the stream position back to the original position when they are done reading the data.  Failure to do so may cause
        /// undesirable results or an exception.
        /// </para>
        /// </remarks>
        public override bool IsReadable(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!stream.CanRead)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_IS_WRITEONLY);
            }

            if (!stream.CanSeek)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_CANNOT_SEEK);
            }

            var wic = new WicUtilities();

            try
            {
                GorgonImageInfo info = wic.GetImageMetaDataFromStream(stream, SupportedFileFormat, null);

                return(info == null ? false : info.Format != BufferFormat.Unknown);
            }
            catch (DX.SharpDXException)
            {
                return(false);
            }
            finally
            {
                wic.Dispose();
            }
        }
Exemple #2
0
        /// <summary>
        /// Function to resize the image to a new width, height and/or depth.
        /// </summary>
        /// <param name="baseImage">The image to resize.</param>
        /// <param name="newWidth">The new width for the image.</param>
        /// <param name="newHeight">The new height for the image (for <see cref="ImageType.Image2D"/> and <see cref="ImageType.ImageCube"/> images).</param>
        /// <param name="newDepth">The new depth for the image (for <see cref="ImageType.Image3D"/> images).</param>
        /// <param name="filter">[Optional] The type of filtering to apply to the scaled image to help smooth larger and smaller images.</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the resized image.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if the <paramref name="newWidth"/>, <paramref name="newHeight"/>, or <paramref name="newDepth"/> parameters are less than 1.</exception>
        /// <exception cref="GorgonException">Thrown if there was an error during resizing regarding image pixel format conversion due to the type of <paramref name="filter"/> applied.</exception>
        /// <remarks>
        /// <para>
        /// This method will change the size of an existing image and return a larger or smaller version of itself as a new <see cref="IGorgonImage"/>.
        /// </para>
        /// </remarks>
        public static IGorgonImage Resize(this IGorgonImage baseImage, int newWidth, int newHeight, int newDepth, ImageFilter filter = ImageFilter.Point)
        {
            if (baseImage == null)
            {
                throw new ArgumentNullException(nameof(baseImage));
            }

            if (newWidth < 1)
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_WIDTH_TOO_SMALL, nameof(newWidth));
            }

            if ((newHeight < 1) && ((baseImage.ImageType == ImageType.Image2D) || (baseImage.ImageType == ImageType.ImageCube)))
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_HEIGHT_TOO_SMALL, nameof(newHeight));
            }

            if ((newDepth < 1) && (baseImage.ImageType == ImageType.Image3D))
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_DEPTH_TOO_SMALL, nameof(newDepth));
            }

            // Only use the appropriate dimensions.
            switch (baseImage.ImageType)
            {
            case ImageType.Image1D:
                newHeight = baseImage.Height;
                break;

            case ImageType.Image2D:
            case ImageType.ImageCube:
                newDepth = baseImage.Depth;
                break;
            }

            // If we haven't actually changed the size, then skip out.
            if ((newWidth == baseImage.Width) && (newHeight == baseImage.Height) && (newDepth == baseImage.Depth))
            {
                return(baseImage);
            }

            var wic = new WicUtilities();

            IGorgonImage newImage = null;

            try
            {
                int calcMipLevels = GorgonImage.CalculateMaxMipCount(newWidth, newHeight, newDepth).Min(baseImage.MipCount);
                newImage = wic.Resize(baseImage, 0, 0, newWidth, newHeight, newDepth, calcMipLevels, filter, ResizeMode.Scale);

                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                newImage?.Dispose();
                wic.Dispose();
            }
        }
        /// <summary>
        /// Function to retrieve the horizontal and vertical offsets for the frames in a multi-frame image.
        /// </summary>
        /// <param name="stream">The stream containing the image data.</param>
        /// <returns>A list of <c>Point</c> values that indicate the offset within the image for each frame.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is <b>null</b>.</exception>
        /// <exception cref="IOException">Thrown when the stream is write-only.
        /// <para>-or-</para>
        /// <para>Thrown when the stream cannot perform seek operations.</para>
        /// </exception>
        /// <exception cref="EndOfStreamException">Thrown when an attempt to read beyond the end of the stream is made.</exception>
        /// <remarks>
        /// <para>
        /// For image codecs that support multiple frames, this reads a list of offset values for each frame so that the frame can be positioned correctly within the base image. If the image does not have
        /// multiple frames, or the codec does not support multiple frames, then an empty list is returned.
        /// </para>
        /// </remarks>
        public IReadOnlyList <DX.Point> GetFrameOffsets(Stream stream)
        {
            if (!SupportsMultipleFrames)
            {
                return(Array.Empty <DX.Point>());
            }

            var wic = new WicUtilities();

            try
            {
                if ((FrameOffsetMetadataNames == null) || (FrameOffsetMetadataNames.Count == 0))
                {
                    return(Array.Empty <DX.Point>());
                }

                IReadOnlyList <DX.Point> result = wic.GetFrameOffsetMetadata(stream, SupportedFileFormat, FrameOffsetMetadataNames);

                if (result == null)
                {
                    throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec));
                }

                return(result);
            }
            catch (DX.SharpDXException)
            {
                throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec));
            }
            finally
            {
                wic.Dispose();
            }
        }
        /// <summary>
        /// Function to persist a <see cref="IGorgonImage"/> to a stream.
        /// </summary>
        /// <param name="imageData">A <see cref="IGorgonImage"/> to persist to the stream.</param>
        /// <param name="stream">The stream that will receive the image data.</param>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="stream"/>, or the <paramref name="imageData"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="stream"/> is read only.</exception>
        /// <exception cref="NotSupportedException">Thrown when the image data in the stream has a pixel format that is unsupported by the codec.</exception>
        /// <remarks>
        /// <para>
        /// When persisting image data via a codec, the image must have a format that the codec can recognize. This list of supported formats is provided by the <see cref="SupportedPixelFormats"/>
        /// property. Applications may convert their image data a supported format before saving the data using a codec.
        /// </para>
        /// </remarks>
        public override void SaveToStream(IGorgonImage imageData, Stream stream)
        {
            if (imageData == null)
            {
                throw new ArgumentNullException(nameof(imageData));
            }

            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!stream.CanWrite)
            {
                throw new IOException(string.Format(Resources.GORIMG_ERR_STREAM_IS_READONLY));
            }

            var wic = new WicUtilities();

            try
            {
                if (SupportedPixelFormats.All(item => item != imageData.Format))
                {
                    throw new NotSupportedException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, imageData.Format));
                }

                IReadOnlyDictionary <string, object> metaData = GetCustomEncodingMetadata(0, imageData);
                wic.EncodeImageData(imageData, stream, SupportedFileFormat, EncodingOptions, metaData);
            }
            finally
            {
                wic.Dispose();
            }
        }
Exemple #5
0
        /// <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="ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is <b>null</b>.</exception>
        /// <exception cref="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="EndOfStreamException">Thrown when an attempt to read beyond the end of the stream is made.</exception>
        /// <remarks>
        /// <para>
        /// This will return the delay (in 1/100ths of a second) between each frame in a multi-frame animated GIF. If the GIF file only has a single frame, then an empty array is returned.
        /// </para>
        /// </remarks>
        public IReadOnlyList <int> GetFrameDelays(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!stream.CanRead)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_IS_WRITEONLY);
            }

            if (!stream.CanSeek)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_CANNOT_SEEK);
            }

            long position = stream.Position;
            var  wic      = new WicUtilities();

            try
            {
                return(wic.GetFrameDelays(stream, SupportedFileFormat, "/grctlext/Delay"));
            }
            finally
            {
                stream.Position = position;
            }
        }
Exemple #6
0
        /// <summary>
        /// Function to crop the image to the rectangle passed to the parameters.
        /// </summary>
        /// <param name="baseImage">The image to resize.</param>
        /// <param name="cropRect">The rectangle that will be used to crop the image.</param>
        /// <param name="newDepth">The new depth for the image (for <see cref="ImageType.Image3D"/> images).</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the resized image.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if the <paramref name="newDepth"/> parameter is less than 1.</exception>
        /// <remarks>
        /// <para>
        /// This method will crop the existing image a smaller version of itself as a new <see cref="IGorgonImage"/>. If the sizes are the same, or the <paramref name="cropRect"/> is larger than the size
        /// of the <paramref name="baseImage"/>, then no changes will be made.
        /// </para>
        /// </remarks>
        public static IGorgonImage Crop(this IGorgonImage baseImage, DX.Rectangle cropRect, int newDepth)
        {
            if (baseImage == null)
            {
                throw new ArgumentNullException(nameof(baseImage));
            }

            if ((newDepth < 1) && (baseImage.ImageType == ImageType.Image3D))
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_DEPTH_TOO_SMALL, nameof(newDepth));
            }

            // Only use the appropriate dimensions.
            switch (baseImage.ImageType)
            {
            case ImageType.Image1D:
                cropRect.Height = baseImage.Height;
                break;

            case ImageType.Image2D:
            case ImageType.ImageCube:
                newDepth = baseImage.Depth;
                break;
            }

            // If the intersection of the crop rectangle and the source buffer are the same (and the depth is the same), then we don't need to crop.
            var bufferRect = new DX.Rectangle(0, 0, baseImage.Width, baseImage.Height);
            var clipRect   = DX.Rectangle.Intersect(cropRect, bufferRect);

            if ((bufferRect.Equals(ref clipRect)) && (newDepth == baseImage.Depth))
            {
                return(baseImage);
            }

            var wic = new WicUtilities();

            IGorgonImage newImage = null;

            try
            {
                int calcMipLevels = GorgonImage.CalculateMaxMipCount(cropRect.Width, cropRect.Height, newDepth).Min(baseImage.MipCount);
                newImage = wic.Resize(baseImage, cropRect.X, cropRect.Y, cropRect.Width, cropRect.Height, newDepth, calcMipLevels, ImageFilter.Point, ResizeMode.Crop);

                // Send the data over to the new image.
                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                newImage?.Dispose();
                wic.Dispose();
            }
        }
Exemple #7
0
        /// <summary>
        /// Function to convert the pixel format of an image into another pixel format.
        /// </summary>
        /// <param name="baseImage">The image to convert.</param>
        /// <param name="format">The new pixel format for the image.</param>
        /// <param name="dithering">[Optional] Flag to indicate the type of dithering to perform when the bit depth for the <paramref name="format"/> is lower than the original bit depth.</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the image data with the converted pixel format.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="format"/> is set to <see cref="BufferFormat.Unknown"/>.
        /// <para>-or-</para>
        /// <para>Thrown when the original format could not be converted into the desired <paramref name="format"/>.</para>
        /// </exception>
        /// <remarks>
        /// <para>
        /// Use this to convert an image format from one to another. The conversion functionality uses Windows Imaging Components (WIC) to perform the conversion.
        /// </para>
        /// <para>
        /// Because this method uses WIC, not all formats will be convertible. To determine if a format can be converted, use the <see cref="GorgonImage.CanConvertToFormat"/> method.
        /// </para>
        /// <para>
        /// For the <see cref="BufferFormat.B4G4R4A4_UNorm"/> format, Gorgon has to perform a manual conversion since that format is not supported by WIC. Because of this, the
        /// <paramref name="dithering"/> flag will be ignored when downsampling to that format.
        /// </para>
        /// </remarks>
        public static IGorgonImage ConvertToFormat(this IGorgonImage baseImage, BufferFormat format, ImageDithering dithering = ImageDithering.None)
        {
            if (baseImage == null)
            {
                throw new ArgumentNullException(nameof(baseImage));
            }

            if ((format == BufferFormat.Unknown) || (!baseImage.CanConvertToFormat(format)))
            {
                throw new ArgumentException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, format), nameof(format));
            }

            if (format == baseImage.Format)
            {
                return(baseImage);
            }

            // If we've asked for 4 bit per channel BGRA, then we have to convert the base image to B8R8G8A8,and then convert manually (no support in WIC).
            if (format == BufferFormat.B4G4R4A4_UNorm)
            {
                return(ConvertToB4G4R4A4(baseImage, dithering));
            }

            // If we're currently using B4G4R4A4, then manually convert (no support in WIC).
            if (baseImage.Format == BufferFormat.B4G4R4A4_UNorm)
            {
                return(ConvertFromB4G4R4A4(baseImage, format));
            }

            var          destInfo = new GorgonFormatInfo(format);
            WicUtilities wic      = null;

            IGorgonImage newImage = null;

            try
            {
                wic      = new WicUtilities();
                newImage = wic.ConvertToFormat(baseImage, format, dithering, baseImage.FormatInfo.IsSRgb, destInfo.IsSRgb);

                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                newImage?.Dispose();
                wic?.Dispose();
            }
        }
Exemple #8
0
        /// <summary>
        /// Function to determine if the source format can convert to any of the formats in the destination list.
        /// </summary>
        /// <param name="sourceFormat">The source format to compare.</param>
        /// <param name="destFormat">List of destination formats to compare.</param>
        /// <returns>An array of formats that the source format can be converted into, or an empty array if no conversion is possible.</returns>
        public static IReadOnlyList <BufferFormat> CanConvertToAny(this BufferFormat sourceFormat, IEnumerable <BufferFormat> destFormat)
        {
            if ((sourceFormat == BufferFormat.Unknown) ||
                (destFormat == null))
            {
                return(Array.Empty <BufferFormat>());
            }

            if (destFormat.All(item => item == sourceFormat))
            {
                return(destFormat.ToArray());
            }

            using (var wic = new WicUtilities())
            {
                return(wic.CanConvertFormats(sourceFormat, destFormat));
            }
        }
        /// <summary>
        /// Function to read file meta data.
        /// </summary>
        /// <param name="stream">Stream used to read the meta data.</param>
        /// <param name="options">Options used for decoding the meta data.</param>
        /// <returns>
        /// The image meta data as a <see cref="GorgonImageInfo"/> value.
        /// </returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is <b>null</b>.</exception>
        /// <exception cref="IOException">Thrown when the stream is write-only.
        /// <para>-or-</para>
        /// <para>Thrown when the stream cannot perform seek operations.</para>
        /// </exception>
        /// <exception cref="EndOfStreamException">Thrown when an attempt to read beyond the end of the stream is made.</exception>
        public IGorgonImageInfo GetMetaData(Stream stream, IGorgonWicDecodingOptions options)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!stream.CanRead)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_IS_WRITEONLY);
            }

            if (!stream.CanSeek)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_CANNOT_SEEK);
            }

            var wic = new WicUtilities();

            try
            {
                // Get our WIC interface.
                GorgonImageInfo result = wic.GetImageMetaDataFromStream(stream, SupportedFileFormat, options);

                if (result.Format == BufferFormat.Unknown)
                {
                    throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, result.Format));
                }

                return(result);
            }
            catch (DX.SharpDXException)
            {
                throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec));
            }
            finally
            {
                wic.Dispose();
            }
        }
        /// <summary>
        /// Function to load an image from a stream.
        /// </summary>
        /// <param name="stream">The stream containing the image data to read.</param>
        /// <param name="size">The size of the image within the stream, in bytes.</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the image data from the stream.</returns>
        /// <exception cref="GorgonException">Thrown when the image data in the stream has a pixel format that is unsupported.</exception>
        protected override IGorgonImage OnDecodeFromStream(Stream stream, long size)
        {
            var    wic         = new WicUtilities();
            Stream streamAlias = stream;

            try
            {
                // If we have a stream position that does not begin exactly at the start of the stream, we have to wrap that stream in a
                // dummy stream wrapper. This is to get around a problem in the underlying COM stream object used by WIC that throws an
                // exception when the stream position is not exactly 0.
                if (streamAlias.Position != 0)
                {
                    streamAlias = new GorgonStreamWrapper(stream, 0, size);
                }

                IGorgonImage result = wic.DecodeImageData(streamAlias, size, SupportedFileFormat, DecodingOptions, FrameOffsetMetadataNames);

                if (result == null)
                {
                    throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec));
                }

                if (stream.Position != streamAlias.Position)
                {
                    stream.Position += streamAlias.Position;
                }

                return(result);
            }
            finally
            {
                if (streamAlias != stream)
                {
                    streamAlias?.Dispose();
                }

                wic.Dispose();
            }
        }
Exemple #11
0
        /// <summary>
        /// Function to expand an image width, height, and/or depth.
        /// </summary>
        /// <param name="baseImage">The image to expand.</param>
        /// <param name="newWidth">The new width of the image.</param>
        /// <param name="newHeight">The new height of the image.</param>
        /// <param name="newDepth">The new depth of the image.</param>
        /// <param name="anchor">[Optional] The anchor point for placing the image data after the image is expanded.</param>
        /// <returns>The expanded image.</returns>
        /// <remarks>
        /// <para>
        /// This will expand the size of an image, but not stretch the actual image data. This will leave a padding around the original image area filled with transparent pixels.
        /// </para>
        /// <para>
        /// The image data can be repositioned in the new image by specifying an <paramref name="anchor"/> point.
        /// </para>
        /// <para>
        /// If the new size of the image is smaller than that of the <paramref name="baseImage"/>, then the new size is constrained to the old size. Cropping is not supported by this method.
        /// </para>
        /// <para>
        /// If a user wishes to resize the image, then call the <see cref="Resize"/> method, of if they wish to crop an image, use the <see cref="Crop"/> method.
        /// </para>
        /// </remarks>
        public static IGorgonImage Expand(this IGorgonImage baseImage, int newWidth, int newHeight, int newDepth, ImageExpandAnchor anchor = ImageExpandAnchor.UpperLeft)
        {
            IGorgonImage workingImage = null;
            WicUtilities wic          = null;

            try
            {
                // Constrain to the correct sizes.
                newWidth  = newWidth.Max(baseImage.Width);
                newHeight = newHeight.Max(baseImage.Height);
                newDepth  = newDepth.Max(baseImage.Depth);

                // Only use the appropriate dimensions.
                switch (baseImage.ImageType)
                {
                case ImageType.Image1D:
                    newHeight = baseImage.Height;
                    break;

                case ImageType.Image2D:
                case ImageType.ImageCube:
                    newDepth = baseImage.Depth;
                    break;
                }

                // We don't shink with this method, use the Crop method for that.
                if ((newWidth <= baseImage.Width) && (newHeight <= baseImage.Height) && (newDepth <= baseImage.Depth))
                {
                    return(baseImage);
                }

                wic = new WicUtilities();

                workingImage = new GorgonImage(new GorgonImageInfo(baseImage)
                {
                    Width  = newWidth,
                    Height = newHeight,
                    Depth  = newDepth
                });

                DX.Point position = DX.Point.Zero;

                switch (anchor)
                {
                case ImageExpandAnchor.UpperMiddle:
                    position = new DX.Point((newWidth / 2) - (baseImage.Width / 2), 0);
                    break;

                case ImageExpandAnchor.UpperRight:
                    position = new DX.Point(newWidth - baseImage.Width, 0);
                    break;

                case ImageExpandAnchor.MiddleLeft:
                    position = new DX.Point(0, (newHeight / 2) - (baseImage.Height / 2));
                    break;

                case ImageExpandAnchor.Center:
                    position = new DX.Point((newWidth / 2) - (baseImage.Width / 2), (newHeight / 2) - (baseImage.Height / 2));
                    break;

                case ImageExpandAnchor.MiddleRight:
                    position = new DX.Point(newWidth - baseImage.Width, (newHeight / 2) - (baseImage.Height / 2));
                    break;

                case ImageExpandAnchor.BottomLeft:
                    position = new DX.Point(0, newHeight - baseImage.Height);
                    break;

                case ImageExpandAnchor.BottomMiddle:
                    position = new DX.Point((newWidth / 2) - (baseImage.Width / 2), newHeight - baseImage.Height);
                    break;

                case ImageExpandAnchor.BottomRight:
                    position = new DX.Point(newWidth - baseImage.Width, newHeight - baseImage.Height);
                    break;
                }

                int calcMipLevels = GorgonImage.CalculateMaxMipCount(newWidth, newHeight, newDepth).Min(baseImage.MipCount);
                workingImage = wic.Resize(baseImage, position.X, position.Y, newWidth, newHeight, newDepth, calcMipLevels, ImageFilter.Point, ResizeMode.Expand);

                // Send the data over to the new image.
                workingImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                workingImage?.Dispose();
                wic?.Dispose();
            }
        }
Exemple #12
0
        /// <summary>
        /// Function to generate a new mip map chain.
        /// </summary>
        /// <param name="baseImage">The image which will have its mip map chain updated.</param>
        /// <param name="mipCount">The number of mip map levels.</param>
        /// <param name="filter">[Optional] The filter to apply when copying the data from one mip level to another.</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the updated mip map data.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> parameter is <b>null</b>.</exception>
        /// <remarks>
        /// <para>
        /// This method will generate a new mip map chain for the <paramref name="mipCount"/>. If the current number of mip maps is not the same as the requested number, then the image buffer will be
        /// adjusted to use the requested number of mip maps. If 0 is passed to <paramref name="mipCount"/>, then a full mip map chain is generated.
        /// </para>
        /// <para>
        /// Note that the <paramref name="mipCount"/> may not be honored depending on the current width, height, and depth of the image. Check the width, height and/or depth property on the returned
        /// <see cref="IGorgonImage"/> to determine how many mip levels were actually generated.
        /// </para>
        /// </remarks>
        public static IGorgonImage GenerateMipMaps(this IGorgonImage baseImage, int mipCount, ImageFilter filter = ImageFilter.Point)
        {
            if (baseImage == null)
            {
                throw new ArgumentNullException(nameof(baseImage));
            }

            int maxMips = GorgonImage.CalculateMaxMipCount(baseImage);

            // If we specify 0, then generate a full chain.
            if ((mipCount <= 0) || (mipCount > maxMips))
            {
                mipCount = maxMips;
            }

            // If we don't have any mip levels, then return the image as-is.
            if (mipCount < 2)
            {
                return(baseImage);
            }

            var destSettings = new GorgonImageInfo(baseImage)
            {
                MipCount = mipCount
            };

            var newImage = new GorgonImage(destSettings);
            var wic      = new WicUtilities();

            try
            {
                // Copy the top mip level from the source image to the dest image.
                for (int array = 0; array < baseImage.ArrayCount; ++array)
                {
                    GorgonNativeBuffer <byte> buffer = newImage.Buffers[0, array].Data;
                    int size = buffer.SizeInBytes;
                    baseImage.Buffers[0, array].Data.CopyTo(buffer, count: size);
                }

                // If we have 4 bits per channel, then we need to convert to 8 bit per channel to make WIC happy.
                if (baseImage.Format == BufferFormat.B4G4R4A4_UNorm)
                {
                    newImage.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm);
                }

                wic.GenerateMipImages(newImage, filter);

                // Convert back if we asked for 4 bit per channel.
                if (baseImage.Format == BufferFormat.B4G4R4A4_UNorm)
                {
                    newImage.ConvertToFormat(BufferFormat.B4G4R4A4_UNorm);
                }

                newImage.CopyTo(baseImage);
                return(baseImage);
            }
            finally
            {
                newImage.Dispose();
                wic.Dispose();
            }
        }