Beispiel #1
0
        /// <summary>
        /// Function to convert the image to B4G4R4A4.
        /// </summary>
        /// <param name="baseImage">The base image to convert.</param>
        /// <param name="dithering">Dithering to apply to the converstion to B8G8R8A8.</param>
        /// <returns>The updated image.</returns>
        private static IGorgonImage ConvertToB4G4R4A4(IGorgonImage baseImage, ImageDithering dithering)
        {
            // This temporary image will be used to convert to B8G8R8A8.
            IGorgonImage newImage = baseImage;

            IGorgonImageInfo destInfo = new GorgonImageInfo(baseImage.ImageType, BufferFormat.B4G4R4A4_UNorm)
            {
                Depth      = baseImage.Depth,
                Height     = baseImage.Height,
                Width      = baseImage.Width,
                ArrayCount = baseImage.ArrayCount,
                MipCount   = baseImage.MipCount
            };

            // This is our working buffer for B4G4R4A4.
            IGorgonImage destImage = new GorgonImage(destInfo);

            try
            {
                // If necessary, convert to B8G8R8A8. Otherwise, we'll just downsample directly.
                if ((newImage.Format != BufferFormat.B8G8R8A8_UNorm) &&
                    (newImage.Format != BufferFormat.R8G8B8A8_UNorm))
                {
                    newImage = baseImage.Clone();
                    ConvertToFormat(newImage, BufferFormat.B8G8R8A8_UNorm, dithering);
                }

                // The next step is to manually downsample to R4G4B4A4.
                // Because we're doing this manually, dithering won't be an option unless unless we've downsampled from a much higher bit format when converting to B8G8R8A8.
                for (int array = 0; array < newImage.ArrayCount; ++array)
                {
                    for (int mip = 0; mip < newImage.MipCount; ++mip)
                    {
                        int depthCount = newImage.GetDepthCount(mip);

                        for (int depth = 0; depth < depthCount; depth++)
                        {
                            IGorgonImageBuffer destBuffer = destImage.Buffers[mip, destInfo.ImageType == ImageType.Image3D ? depth : array];
                            IGorgonImageBuffer srcBuffer  = newImage.Buffers[mip, newImage.ImageType == ImageType.Image3D ? depth : array];

                            ConvertPixelsToB4G4R4A4(destBuffer, srcBuffer);
                        }
                    }
                }

                destImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                destImage.Dispose();

                if (newImage != baseImage)
                {
                    newImage.Dispose();
                }
            }
        }
Beispiel #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();
            }
        }
Beispiel #3
0
        /// <summary>
        /// Function to convert the image from B4G4R4A4.
        /// </summary>
        /// <param name="baseImage">The base image to convert.</param>
        /// <param name="destFormat">The destination format.</param>
        /// <returns>The updated image.</returns>
        private static IGorgonImage ConvertFromB4G4R4A4(IGorgonImage baseImage, BufferFormat destFormat)
        {
            // If we're converting to R8G8B8A8 or B8G8R8A8, then use those formats, otherwise, default to B8G8R8A8 as an intermediate buffer.
            BufferFormat tempFormat = ((destFormat != BufferFormat.B8G8R8A8_UNorm) && (destFormat != BufferFormat.R8G8B8A8_UNorm)) ? BufferFormat.B8G8R8A8_UNorm : destFormat;

            // Create an worker image in B8G8R8A8 format.
            IGorgonImageInfo destInfo = new GorgonImageInfo(baseImage.ImageType, tempFormat)
            {
                Depth      = baseImage.Depth,
                Height     = baseImage.Height,
                Width      = baseImage.Width,
                ArrayCount = baseImage.ArrayCount,
                MipCount   = baseImage.MipCount
            };

            // Our destination image for B8G8R8A8 or R8G8B8A8.
            var destImage = new GorgonImage(destInfo);

            try
            {
                // We have to manually upsample from R4G4B4A4 to B8R8G8A8.
                // Because we're doing this manually, dithering won't be an option unless
                for (int array = 0; array < baseImage.ArrayCount; ++array)
                {
                    for (int mip = 0; mip < baseImage.MipCount; ++mip)
                    {
                        int depthCount = baseImage.GetDepthCount(mip);

                        for (int depth = 0; depth < depthCount; depth++)
                        {
                            IGorgonImageBuffer destBuffer = destImage.Buffers[mip, baseImage.ImageType == ImageType.Image3D ? depth : array];
                            IGorgonImageBuffer srcBuffer  = baseImage.Buffers[mip, baseImage.ImageType == ImageType.Image3D ? depth : array];

                            ConvertPixelsFromB4G4R4A4(destBuffer, srcBuffer);
                        }
                    }
                }

                // If the destination format is not R8G8B8A8 or B8G8R8A8, then we need to do more conversion.
                if (destFormat != destImage.Format)
                {
                    ConvertToFormat(destImage, destFormat);
                }

                // Update the base image with our worker image.
                destImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                destImage.Dispose();
            }
        }
Beispiel #4
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 image format is compressed.</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>
        /// If the image does not contain alpha then the method will return right away and no alterations to the image will be performed.
        /// </para>
        /// </remarks>
        public static IGorgonImage ConvertToPremultipliedAlpha(this IGorgonImage baseImage)
        {
            IGorgonImage newImage = null;
            GorgonNativeBuffer <byte> imageData = null;

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

            if (!baseImage.FormatInfo.HasAlpha)
            {
                return(baseImage);
            }

            if (baseImage.FormatInfo.IsCompressed)
            {
                throw new ArgumentException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, baseImage.Format), nameof(baseImage));
            }

            try
            {
                var cloneImageInfo = new GorgonImageInfo(baseImage)
                {
                    HasPreMultipliedAlpha = true
                };
                imageData = new GorgonNativeBuffer <byte>(baseImage.ImageData.Length);
                baseImage.ImageData.CopyTo(imageData);

                unsafe
                {
                    newImage = new GorgonImage(cloneImageInfo, new GorgonReadOnlyPointer((void *)imageData, imageData.SizeInBytes));

                    int arrayOrDepth = newImage.ImageType == ImageType.Image3D ? newImage.Depth : newImage.ArrayCount;

                    for (int mip = 0; mip < newImage.MipCount; ++mip)
                    {
                        for (int i = 0; i < arrayOrDepth; ++i)
                        {
                            IGorgonImageBuffer buffer = newImage.Buffers[mip, i];
                            byte *ptr      = (byte *)buffer.Data;
                            int   rowPitch = buffer.PitchInformation.RowPitch;

                            for (int y = 0; y < buffer.Height; ++y)
                            {
                                ImageUtilities.SetPremultipliedScanline(ptr, rowPitch, ptr, rowPitch, buffer.Format);
                                ptr += rowPitch;
                            }
                        }
                    }
                }

                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                imageData?.Dispose();
                newImage?.Dispose();
            }
        }
Beispiel #5
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();
            }
        }