Exemplo n.º 1
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();
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Function to convert the image data into a premultiplied format.
        /// </summary>
        /// <param name="baseImage">The image to convert.</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the image data with the premultiplied alpha pixel data.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> is <b>null</b>.</exception>
        /// <exception cref="ArgumentException">Thrown when the original format could not be converted to <see cref="BufferFormat.R8G8B8A8_UNorm"/>.</exception>
        /// <remarks>
        /// <para>
        /// Use this to convert an image to a premultiplied format. This takes each Red, Green and Blue element and multiplies them by the Alpha element.
        /// </para>
        /// <para>
        /// Because this method will only operate on <see cref="BufferFormat.R8G8B8A8_UNorm"/> formattted image data, the image will be converted to that format and converted back to its original format
        /// after the alpha is premultiplied. This may cause color fidelity issues. If the image cannot be converted, then an exception will be thrown.
        /// </para>
        /// </remarks>
        public static IGorgonImage ConvertToPremultipliedAlpha(this IGorgonImage baseImage)
        {
            IGorgonImage newImage = null;

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

            try
            {
                // Worker image.
                var cloneImageInfo = new GorgonImageInfo(baseImage)
                {
                    HasPreMultipliedAlpha = true
                };
                newImage = new GorgonImage(cloneImageInfo);
                baseImage.CopyTo(newImage);

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

                    // Clone the image so we can convert it to the correct format.
                    newImage.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm);
                }

                unsafe
                {
                    int *imagePtr = (int *)(newImage.ImageData);

                    for (int i = 0; i < newImage.SizeInBytes; i += newImage.FormatInfo.SizeInBytes)
                    {
                        var color = GorgonColor.FromABGR(*imagePtr);
                        color         = new GorgonColor(color.Red * color.Alpha, color.Green * color.Alpha, color.Blue * color.Alpha, color.Alpha);
                        *(imagePtr++) = color.ToABGR();
                    }
                }

                if (newImage.Format != baseImage.Format)
                {
                    newImage.ConvertToFormat(baseImage.Format);
                }

                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                newImage?.Dispose();
            }
        }
Exemplo n.º 3
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();
            }
        }
Exemplo n.º 4
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();
            }
        }