Beispiel #1
0
        /// <summary>
        /// Function to convert the pixel data in the buffers from B8G8R4A8 (or R8G8B8A8) to B4G4R4A4.
        /// </summary>
        /// <param name="dest">The destination buffer to receive the newly formatted data.</param>
        /// <param name="src">The source buffer to containing the source pixels to convert.</param>
        private static unsafe void ConvertPixelsToB4G4R4A4(IGorgonImageBuffer dest, IGorgonImageBuffer src)
        {
            ushort *destBufferPtr = (ushort *)dest.Data;
            uint *  srcBufferPtr  = (uint *)src.Data;

            for (int i = 0; i < src.PitchInformation.SlicePitch; i += sizeof(uint))
            {
                uint srcPixel = *(srcBufferPtr++);
                uint b, g, r, a;

                if (src.Format == BufferFormat.B8G8R8A8_UNorm)
                {
                    a = ((srcPixel >> 24) & 0xff) >> 4;
                    r = ((srcPixel >> 16) & 0xff) >> 4;
                    g = ((srcPixel >> 8) & 0xff) >> 4;
                    b = (srcPixel & 0xff) >> 4;
                }
                else // Convert from R8G8B8A8.
                {
                    a = ((srcPixel >> 24) & 0xff) >> 4;
                    b = ((srcPixel >> 16) & 0xff) >> 4;
                    g = ((srcPixel >> 8) & 0xff) >> 4;
                    r = (srcPixel & 0xff) >> 4;
                }

                *(destBufferPtr++) = (ushort)((a << 12) | (r << 8) | (g << 4) | b);
            }
        }
Beispiel #2
0
        /// <summary>
        /// Function to retrieve an image based on the current depth slice.
        /// </summary>
        /// <param name="sourceImage">The image to extract the depth slice from.</param>
        /// <param name="currentDepthSlice">The depth slice to extract.</param>
        /// <returns>A new image with the specified depth slice.</returns>
        public IGorgonImage GetDepthSliceAsImage(IGorgonImage sourceImage, int currentDepthSlice)
        {
            if (sourceImage.MipCount == 1)
            {
                return(sourceImage);
            }

            IGorgonImage result = new GorgonImage(new GorgonImageInfo(sourceImage, ImageType.Image2D)
            {
                Depth = 1
            });

            for (int mip = 0; mip < result.MipCount; ++mip)
            {
                int depthCount = sourceImage.GetDepthCount(mip);

                if (currentDepthSlice >= depthCount)
                {
                    return(result);
                }

                IGorgonImageBuffer srcBuffer  = sourceImage.Buffers[mip, currentDepthSlice];
                IGorgonImageBuffer destBuffer = result.Buffers[mip];

                srcBuffer.CopyTo(destBuffer);
            }

            return(result);
        }
Beispiel #3
0
        /// <summary>
        /// Function to convert the pixel data in the buffers from B4G4R4A4 to B8G8R4A8 or R8G8B8A8.
        /// </summary>
        /// <param name="dest">The destination buffer to receive the newly formatted data.</param>
        /// <param name="src">The source buffer to containing the source pixels to convert.</param>
        private static unsafe void ConvertPixelsFromB4G4R4A4(IGorgonImageBuffer dest, IGorgonImageBuffer src)
        {
            ushort *srcBufferPtr  = (ushort *)src.Data;
            uint *  destBufferPtr = (uint *)dest.Data;

            for (int i = 0; i < src.PitchInformation.SlicePitch; i += sizeof(ushort))
            {
                ushort srcPixel = *(srcBufferPtr++);

                int b = ((srcPixel >> 12) & 0xf);
                int g = ((srcPixel >> 8) & 0xf);
                int r = ((srcPixel >> 4) & 0xf);
                int a = (srcPixel & 0xf);

                // Adjust the values to fill out a 32 bit integer: If r == 0xc in the 16 bit format, then r == 0xcc in the 32 bit format by taking the value and
                // shifting it left by 4 bits and OR'ing the original r value again. ((0xc << 4 = 0xc0) OR 0xc = 0xcc).
                a = ((a << 4) | a);
                r = ((r << 4) | r);
                g = ((g << 4) | g);
                b = ((b << 4) | b);

                uint value = (uint)((dest.Format == BufferFormat.B8G8R8A8_UNorm)
                                    ? ((b << 24) | (g << 16) | (r << 8) | a)
                                    // Convert to R8G8B8A8 (flipped for little endian).
                                    : ((b << 24) | (a << 16) | (r << 8) | g));
                *(destBufferPtr++) = value;
            }
        }
Beispiel #4
0
        /// <summary>
        /// Function to retrieve an image based on the current mip level.
        /// </summary>
        /// <param name="sourceImage">The image to extract the mip level from.</param>
        /// <param name="currentMipLevel">The mip level to extract.</param>
        /// <returns>A new image with the specified mip level.</returns>
        public IGorgonImage GetMipLevelAsImage(IGorgonImage sourceImage, int currentMipLevel)
        {
            if (sourceImage.MipCount == 1)
            {
                return(sourceImage);
            }

            int depthCount = 1;

            if (sourceImage.ImageType == ImageType.Image3D)
            {
                depthCount = sourceImage.GetDepthCount(currentMipLevel);
            }

            IGorgonImage result = new GorgonImage(new GorgonImageInfo(sourceImage)
            {
                Width    = sourceImage.Buffers[currentMipLevel].Width,
                Height   = sourceImage.Buffers[currentMipLevel].Height,
                Depth    = depthCount,
                MipCount = 1
            });

            for (int array = 0; array < sourceImage.ArrayCount; ++array)
            {
                for (int depth = 0; depth < result.Depth; ++depth)
                {
                    IGorgonImageBuffer srcBuffer  = sourceImage.Buffers[currentMipLevel, sourceImage.ImageType == ImageType.Image3D ? depth : array];
                    IGorgonImageBuffer destBuffer = result.Buffers[0, sourceImage.ImageType == ImageType.Image3D ? depth : array];

                    srcBuffer.CopyTo(destBuffer);
                }
            }

            return(result);
        }
Beispiel #5
0
        /// <summary>
        /// Function to transfer a 24 bit rgb image into a <see cref="IGorgonImageBuffer"/>.
        /// </summary>
        /// <param name="bitmapLock">The lock on the bitmap to transfer from.</param>
        /// <param name="buffer">The buffer to transfer into.</param>
        private static void Transfer24Rgb(BitmapData bitmapLock, IGorgonImageBuffer buffer)
        {
            unsafe
            {
                byte *pixels = (byte *)bitmapLock.Scan0.ToPointer();

                for (int y = 0; y < bitmapLock.Height; y++)
                {
                    // We only need the width here, as our pointer will handle the stride by virtue of being an int.
                    byte *offset = pixels + (y * bitmapLock.Stride);

                    int destOffset = y * buffer.PitchInformation.RowPitch;
                    for (int x = 0; x < bitmapLock.Width; x++)
                    {
                        // The DXGI format nomenclature is a little confusing as we tend to think of the layout as being highest to
                        // lowest, but in fact, it is lowest to highest.
                        // So, we must convert to ABGR even though the DXGI format is RGBA. The memory layout is from lowest
                        // (R at byte 0) to the highest byte (A at byte 3).
                        // Thus, R is the lowest byte, and A is the highest: A(24), B(16), G(8), R(0).
                        byte b = *offset++;
                        byte g = *offset++;
                        byte r = *offset++;

                        var  color      = new GorgonColor(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
                        int *destBuffer = (int *)(Unsafe.AsPointer(ref buffer.Data[destOffset]));
                        *    destBuffer = color.ToABGR();
                        destOffset += 4;
                    }
                }
            }
        }
Beispiel #6
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 #7
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 #8
0
        /// <summary>
        /// Function to set the alpha channel for a specific buffer in the image.
        /// </summary>
        /// <param name="buffer">The buffer to set the alpha channel on.</param>
        /// <param name="alphaValue">The value to set.</param>
        /// <param name="updateAlphaRange">[Optional] The range of alpha values in the buffer that will be updated.</param>
        /// <param name="region">[Optional] The region in the buffer to update.</param>
        /// <returns>The fluent interface for the buffer that was updated.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="buffer"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentException">Thrown if the buffer format is compressed.</exception>
        /// <remarks>
        /// <para>
        /// This will set the alpha channel for the image data in the <paramref name="buffer"/> to a discrete value specified by <paramref name="alphaValue"/>.
        /// </para>
        /// <para>
        /// If the <paramref name="updateAlphaRange"/> parameter is set, then the alpha values in the <paramref name="buffer"/> will be examined and if the alpha value is less than the minimum range or
        /// greater than the maximum range, then the <paramref name="alphaValue"/> will <b>not</b> be set on the alpha channel.
        /// </para>
        /// <para>
        /// If the <paramref name="region"/> is not specified, then the entire buffer is updated, otherwise only the values within the <paramref name="region"/> are updated.
        /// </para>
        /// </remarks>
        public static IGorgonImageBuffer SetAlpha(this IGorgonImageBuffer buffer, float alphaValue, GorgonRangeF?updateAlphaRange = null, DX.Rectangle?region = null)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            // If we don't have an alpha channel, then don't do anything.
            if (!buffer.FormatInformation.HasAlpha)
            {
                return(buffer);
            }

            // We don't support compressed formats.
            if (buffer.FormatInformation.IsCompressed)
            {
                throw new ArgumentException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, buffer.Format), nameof(buffer));
            }

            if (updateAlphaRange == null)
            {
                updateAlphaRange = new GorgonRangeF(0, 1);
            }

            var fullRect = new DX.Rectangle(0, 0, buffer.Width - 1, buffer.Height - 1);

            if (region == null)
            {
                region = fullRect;
            }
            else
            {
                region = DX.Rectangle.Intersect(region.Value, fullRect);
            }

            unsafe
            {
                byte *src   = (byte *)buffer.Data;
                uint  alpha = (uint)(alphaValue * 255.0f);
                uint  min   = (uint)(updateAlphaRange.Value.Minimum * 255.0f);
                uint  max   = (uint)(updateAlphaRange.Value.Maximum * 255.0f);
                int   pitch = buffer.PitchInformation.RowPitch / buffer.Width;

                for (int y = region.Value.Top; y <= region.Value.Bottom; ++y)
                {
                    byte *horzPtr = src + region.Value.Left * buffer.FormatInformation.SizeInBytes;
                    ImageUtilities.SetAlphaScanline(horzPtr, region.Value.Width * pitch, horzPtr, region.Value.Width * pitch, buffer.Format, alpha, min, max);
                    src += buffer.PitchInformation.RowPitch;
                }
            }

            return(buffer);
        }
Beispiel #9
0
        /// <summary>
        /// Function to copy an image onto another image, using the supplied alignment.
        /// </summary>
        /// <param name="srcImage">The image to copy.</param>
        /// <param name="destImage">The destination image.</param>
        /// <param name="startMip">The starting mip map level to copy.</param>
        /// <param name="startArrayOrDepth">The starting array index for 2D images, or depth slice for 3D images.</param>
        /// <param name="alignment">The alignment of the image, relative to the source image.</param>
        public void CopyTo(IGorgonImage srcImage, IGorgonImage destImage, int startMip, int startArrayOrDepth, Alignment alignment)
        {
            int mipCount   = destImage.MipCount - startMip;
            int arrayCount = destImage.ArrayCount - (destImage.ImageType == ImageType.Image3D ? 0 : startArrayOrDepth);

            int minMipCount   = mipCount.Min(srcImage.MipCount);
            int minArrayCount = arrayCount.Min(srcImage.ArrayCount);

            var size = new DX.Size2(srcImage.Width, srcImage.Height);

            for (int array = 0; array < minArrayCount; ++array)
            {
                for (int mip = 0; mip < minMipCount; ++mip)
                {
                    int destDepthCount = destImage.GetDepthCount(mip + startMip);
                    int minDepth       = destDepthCount.Min(srcImage.GetDepthCount(mip));

                    for (int depth = 0; depth < minDepth; ++depth)
                    {
                        int destOffset;
                        if (destImage.ImageType == ImageType.Image3D)
                        {
                            destOffset = depth + startArrayOrDepth;

                            // We're at the end of the destination buffer, skip the rest of the slices.
                            if (destOffset >= destDepthCount)
                            {
                                break;
                            }
                        }
                        else
                        {
                            destOffset = array + startArrayOrDepth;
                        }

                        IGorgonImageBuffer srcBuffer  = srcImage.Buffers[mip, srcImage.ImageType == ImageType.Image3D ? depth : array];
                        IGorgonImageBuffer destBuffer = destImage.Buffers[mip + startMip, destOffset];

                        // Clear the destination buffer before copying.
                        destBuffer.Data.Fill(0);

                        int minWidth   = destBuffer.Width.Min(srcBuffer.Width);
                        int minHeight  = destBuffer.Height.Min(srcBuffer.Height);
                        var copyRegion = new DX.Rectangle(0, 0, minWidth, minHeight);

                        DX.Point startLoc = GetAnchorStart(new DX.Size2(minWidth, minHeight), ref size, alignment);

                        srcBuffer.CopyTo(destBuffer, copyRegion, startLoc.X, startLoc.Y);
                    }
                }
            }
        }
        /// <summary>
        /// Function to detect if a sprite has no pixel data.
        /// </summary>
        /// <param name="bounds">The bounds of the sprite on the texture.</param>
        /// <param name="imageData">The system memory representation of the texture.</param>
        /// <param name="skipMask">The color used to mask which pixels are considered empty.</param>
        /// <returns><b>true</b> if sprite is empty, <b>false</b> if not.</returns>
        private bool IsEmpty(DX.Rectangle bounds, IGorgonImageBuffer imageData, GorgonColor skipMask)
        {
            int pixelSize = imageData.FormatInformation.SizeInBytes;
            int color     = skipMask.ToABGR();

            for (int y = bounds.Y; y < bounds.Bottom; ++y)
            {
                for (int x = bounds.X; x < bounds.Right; ++x)
                {
                    // The last byte of the pixel should be the alpha (we force conversion to R8G8B8A8).
                    int value = imageData.Data.ReadAs <int>((y * imageData.PitchInformation.RowPitch) + (x * pixelSize));
                    if (color != value)
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
Beispiel #11
0
        /// <summary>
        /// Function to update the number of mip levels on an image.
        /// </summary>
        /// <param name="sourceImage">The source image to update.</param>
        /// <param name="newMipCount">The new number of mip levels for the resulting image.</param>
        /// <returns>The updated image, or the same image if no changes were made.</returns>
        public IGorgonImage ChangeMipCount(IGorgonImage sourceImage, int newMipCount)
        {
            if (sourceImage.MipCount == newMipCount)
            {
                return(sourceImage);
            }

            int maxDepth    = sourceImage.Depth;
            int minMipCount = newMipCount
                              .Min(sourceImage.MipCount)
                              .Min(GorgonImage.CalculateMaxMipCount(sourceImage.Width, sourceImage.Height, sourceImage.Depth));

            IGorgonImage result = new GorgonImage(new GorgonImageInfo(sourceImage)
            {
                MipCount = newMipCount
            });

            for (int array = 0; array < sourceImage.ArrayCount; ++array)
            {
                for (int mip = 0; mip < minMipCount; ++mip)
                {
                    for (int depth = 0; depth < maxDepth; ++depth)
                    {
                        int depthOrArray        = sourceImage.ImageType == ImageType.Image3D ? depth : array;
                        IGorgonImageBuffer src  = sourceImage.Buffers[mip, depthOrArray];
                        IGorgonImageBuffer dest = result.Buffers[mip, depthOrArray];
                        src.CopyTo(dest);
                    }

                    maxDepth >>= 1;
                    if (maxDepth < 1)
                    {
                        maxDepth = 1;
                    }
                }
            }

            return(result);
        }
Beispiel #12
0
        /// <summary>
        /// Function to retrieve an image based on the array index.
        /// </summary>
        /// <param name="sourceImage">The image to extract the array index from.</param>
        /// <param name="currentArrayIndex">The array index to extract.</param>
        /// <returns>A new image with the specified array index.</returns>
        public IGorgonImage GetArrayIndexAsImage(IGorgonImage sourceImage, int currentArrayIndex)
        {
            if (sourceImage.ArrayCount == 1)
            {
                return(sourceImage);
            }

            IGorgonImage result = new GorgonImage(new GorgonImageInfo(sourceImage)
            {
                ArrayCount = 1
            });

            for (int mip = 0; mip < result.MipCount; ++mip)
            {
                IGorgonImageBuffer srcBuffer  = sourceImage.Buffers[mip, currentArrayIndex];
                IGorgonImageBuffer destBuffer = result.Buffers[mip, 0];

                srcBuffer.CopyTo(destBuffer);
            }

            return(result);
        }
Beispiel #13
0
        /// <summary>
        /// Function to update the depth slice count for a 3D image, or the array index count for a 2D/cube image.
        /// </summary>
        /// <param name="sourceImage">The image to update.</param>
        /// <param name="arrayOrDepthCount">The new depth or array count.</param>
        /// <returns>A new image with the specified depth/array count, or the same image if no changes were made.</returns>
        public IGorgonImage ChangeArrayOrDepthCount(IGorgonImage sourceImage, int arrayOrDepthCount)
        {
            int currentArrayOrDepth = sourceImage.ImageType == ImageType.Image3D ? sourceImage.Depth : sourceImage.ArrayCount;
            int depthCount          = sourceImage.ImageType == ImageType.Image3D ? arrayOrDepthCount : 1;
            int arrayCount          = sourceImage.ImageType != ImageType.Image3D ? arrayOrDepthCount : 1;

            if (currentArrayOrDepth == arrayOrDepthCount)
            {
                return(sourceImage);
            }

            IGorgonImage newImage = new GorgonImage(new GorgonImageInfo(sourceImage)
            {
                Depth      = depthCount,
                ArrayCount = arrayCount
            });

            for (int array = 0; array < arrayCount.Min(sourceImage.ArrayCount); ++array)
            {
                for (int mip = 0; mip < sourceImage.MipCount; ++mip)
                {
                    for (int depth = 0; depth < depthCount.Min(sourceImage.GetDepthCount(mip)); ++depth)
                    {
                        IGorgonImageBuffer srcBuffer  = sourceImage.Buffers[mip, sourceImage.ImageType == ImageType.Image3D ? depth : array];
                        IGorgonImageBuffer destBuffer = newImage.Buffers[mip, sourceImage.ImageType == ImageType.Image3D ? depth : array];
                        srcBuffer.CopyTo(destBuffer);
                    }

                    depthCount >>= 1;

                    if (depthCount < 1)
                    {
                        depthCount = 1;
                    }
                }
            }

            return(newImage);
        }
Beispiel #14
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 #15
0
        /// <summary>
        /// Function to copy the contents of a GDI+ bitmap object to an individual <see cref="IGorgonImageBuffer"/>.
        /// </summary>
        /// <param name="bitmap">The bitmap to convert.</param>
        /// <param name="buffer">The buffer to that will receive the image data.</param>
        /// <returns>A new GDI+ bitmap object.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="bitmap"/>, or the <paramref name="buffer"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentException">Thrown if the <paramref name="bitmap"/> and the <paramref name="buffer"/> do not have the same width and height.</exception>
        /// <exception cref="GorgonException">Thrown if the <paramref name="buffer"/> is not a 32 bit <c>R8G8B8A8</c> format, or <c>B8G8R8*</c> format.
        /// <para>-or-</para>
        /// <para>Thrown when the <paramref name="bitmap"/> is not in a 32 bit ARGB format.</para>
        /// </exception>
        /// <remarks>
        /// <para>
        /// This method will take a <see cref="IGorgonImageBuffer"/> and copy its data into a new 2D <see cref="Bitmap"/>. The <paramref name="buffer"/> and the <paramref name="bitmap"/> must have
        /// an identical width and height. Otherwise, an exception will be thrown.
        /// </para>
        /// <para>
        /// Some format conversion is performed on the <paramref name="buffer"/> when it is imported. The format conversion will always convert to a pixel format of <c>Format32bppArgb</c> or
        /// <c>Format24bppRgb</c>.  The following formats are supported for 32 bit conversion:
        /// <list type="bullet">
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_UNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_UNorm_SRgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_SInt"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_SNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_UInt"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_Typeless"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8A8_UNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8A8_UNorm_SRgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8A8_Typeless"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8X8_UNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8X8_UNorm_SRgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8X8_Typeless"/></term>
        ///     </item>
        /// </list>
        /// </para>
        /// <para>
        /// If the source <paramref name="buffer"/> does not support any of the formats on the lists, then an exception will be thrown.
        /// </para>
        /// </remarks>
        public static void CopyTo(this Bitmap bitmap, IGorgonImageBuffer buffer)
        {
            if (bitmap == null)
            {
                throw new ArgumentNullException(nameof(bitmap));
            }

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

            if ((bitmap.PixelFormat != PixelFormat.Format32bppArgb) &&
                (bitmap.PixelFormat != PixelFormat.Format32bppPArgb))
            {
                throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, bitmap.PixelFormat));
            }

            if ((bitmap.Width != buffer.Width) || (bitmap.Height != buffer.Height))
            {
                throw new ArgumentException(string.Format(Resources.GORIMG_ERR_BITMAP_SIZE_NOT_CORRECT, bitmap.Width, bitmap.Height, buffer.Width, buffer.Height));
            }

            bool needsSwizzle;

            switch (buffer.Format)
            {
            case BufferFormat.R8G8B8A8_UNorm:
            case BufferFormat.R8G8B8A8_UNorm_SRgb:
            case BufferFormat.R8G8B8A8_SInt:
            case BufferFormat.R8G8B8A8_SNorm:
            case BufferFormat.R8G8B8A8_UInt:
            case BufferFormat.R8G8B8A8_Typeless:
                needsSwizzle = true;
                break;

            case BufferFormat.B8G8R8A8_UNorm:
            case BufferFormat.B8G8R8A8_Typeless:
            case BufferFormat.B8G8R8A8_UNorm_SRgb:
            case BufferFormat.B8G8R8X8_UNorm:
            case BufferFormat.B8G8R8X8_Typeless:
            case BufferFormat.B8G8R8X8_UNorm_SRgb:
                needsSwizzle = false;
                break;

            default:
                throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, buffer.Format));
            }

            unsafe
            {
                BitmapData srcData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);

                try
                {
                    byte *srcPtr  = (byte *)srcData.Scan0;
                    byte *destPtr = (byte *)buffer.Data;

                    for (int y = 0; y < buffer.Height; ++y)
                    {
                        byte *src  = srcPtr + (y * srcData.Stride);
                        byte *dest = destPtr + (y * buffer.PitchInformation.RowPitch);

                        if (!needsSwizzle)
                        {
                            Unsafe.CopyBlock(dest, src, (uint)(srcData.Stride.Min(buffer.PitchInformation.RowPitch)));
                            continue;
                        }

                        ImageUtilities.SwizzleScanline(src, buffer.PitchInformation.RowPitch, dest, srcData.Stride, buffer.Format, ImageBitFlags.None);
                    }
                }
                finally
                {
                    bitmap.UnlockBits(srcData);
                }
            }
        }
Beispiel #16
0
        /// <summary>
        /// Function to perform the copying of image data into the buffer.
        /// </summary>
        /// <param name="reader">A reader used to read the data from the source stream.</param>
        /// <param name="image">Image data.</param>
        /// <param name="conversionFlags">Flags used to convert the image.</param>
        private void CopyImageData(GorgonBinaryReader reader, IGorgonImage image, TGAConversionFlags conversionFlags)
        {
            // TGA only supports 1 array level, and 1 mip level, so we only need to get the first buffer.
            IGorgonImageBuffer buffer = image.Buffers[0];

            // Determine how large a row is, in bytes.
            var formatInfo = new GorgonFormatInfo(image.Format);

            GorgonPitchLayout srcPitch = (conversionFlags & TGAConversionFlags.Expand) == TGAConversionFlags.Expand
                                             ? new GorgonPitchLayout(image.Width * 3, image.Width * 3 * image.Height)
                                             : formatInfo.GetPitchForFormat(image.Width, image.Height);

            unsafe
            {
                // Otherwise, allocate a buffer for conversion.
                byte *destPtr = (byte *)buffer.Data;

                // Adjust destination for inverted axes.
                if ((conversionFlags & TGAConversionFlags.InvertX) == TGAConversionFlags.InvertX)
                {
                    destPtr += buffer.PitchInformation.RowPitch - formatInfo.SizeInBytes;
                }

                if ((conversionFlags & TGAConversionFlags.InvertY) != TGAConversionFlags.InvertY)
                {
                    destPtr += (image.Height - 1) * buffer.PitchInformation.RowPitch;
                }

                // Used to counter the number of lines to force as opaque.
                int opaqueLineCount = 0;
                // The buffer used to hold an uncompressed scanline.
                GorgonNativeBuffer <byte> lineBuffer = null;

                try
                {
                    for (int y = 0; y < image.Height; y++)
                    {
                        // Indicates that the scanline has an alpha of 0 for the entire run.
                        bool lineHasZeroAlpha;

                        if ((conversionFlags & TGAConversionFlags.RLE) == TGAConversionFlags.RLE)
                        {
                            lineHasZeroAlpha = ReadCompressed(reader, image.Width, destPtr, image.Format, conversionFlags);
                        }
                        else
                        {
                            // Read the current scanline into memory.
                            if (lineBuffer == null)
                            {
                                lineBuffer = new GorgonNativeBuffer <byte>(srcPitch.RowPitch);
                            }

                            reader.ReadRange(lineBuffer, count: srcPitch.RowPitch);

                            lineHasZeroAlpha = ReadUncompressed((byte *)lineBuffer, srcPitch.RowPitch, destPtr, image.Format, conversionFlags);
                        }

                        if ((lineHasZeroAlpha) && ((conversionFlags & TGAConversionFlags.SetOpaqueAlpha) == TGAConversionFlags.SetOpaqueAlpha))
                        {
                            opaqueLineCount++;
                        }

                        // The components of the pixel data in a TGA file need swizzling for 32 bit.
                        if (formatInfo.BitDepth == 32)
                        {
                            ImageUtilities.SwizzleScanline(destPtr,
                                                           buffer.PitchInformation.RowPitch,
                                                           destPtr,
                                                           buffer.PitchInformation.RowPitch,
                                                           image.Format,
                                                           ImageBitFlags.None);
                        }

                        if ((conversionFlags & TGAConversionFlags.InvertY) != TGAConversionFlags.InvertY)
                        {
                            destPtr -= buffer.PitchInformation.RowPitch;
                        }
                        else
                        {
                            destPtr += buffer.PitchInformation.RowPitch;
                        }
                    }
                }
                finally
                {
                    lineBuffer?.Dispose();
                }

                if (opaqueLineCount != image.Height)
                {
                    return;
                }

                // Set the alpha to opaque if we don't have any alpha values (i.e. alpha = 0 for all pixels).
                destPtr = (byte *)buffer.Data;
                for (int y = 0; y < image.Height; y++)
                {
                    ImageUtilities.CopyScanline(destPtr,
                                                buffer.PitchInformation.RowPitch,
                                                destPtr,
                                                buffer.PitchInformation.RowPitch,
                                                image.Format,
                                                ImageBitFlags.OpaqueAlpha);
                    destPtr += buffer.PitchInformation.RowPitch;
                }
            }
        }
 /// <summary>
 /// Function to copy the image buffer data from this buffer into another.
 /// </summary>
 /// <param name="buffer">The buffer to copy into.</param>
 /// <param name="sourceRegion">[Optional] The region in the source to copy.</param>
 /// <param name="destX">[Optional] Horizontal offset in the destination buffer.</param>
 /// <param name="destY">[Optional] Vertical offset in the destination buffer.</param>
 /// <exception cref="ArgumentNullException">Thrown when the <paramref name="buffer" /> parameter is <b>null</b>.</exception>
 /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="buffer"/> has no data.</exception>
 /// <exception cref="ArgumentException">Thrown when the <paramref name="buffer" /> is not the same format as this buffer.</exception>
 /// <exception cref="ArgumentOutOfRangeException">Thrown when the source region does not fit within the bounds of this buffer.</exception>
 /// <remarks>
 /// <para>
 /// This method will copy the contents of this buffer into another buffer and will provide clipping to handle cases where the buffer or <paramref name="sourceRegion" /> is mismatched with the
 /// destination size. If this buffer, and the buffer passed to <paramref name="buffer"/> share the same pointer address, then this method will return immediately without making any changes.
 /// </para>
 /// <para>
 /// Users may define an area on this buffer to copy by specifying the <paramref name="sourceRegion" /> parameter. If <b>null</b> is passed to this parameter, then the entire buffer will be copied
 /// to the destination.
 /// </para>
 /// <para>
 /// An offset into the destination buffer may also be specified to relocate the data copied from this buffer into the destination.  Clipping will be applied if the offset pushes the source data
 /// outside of the boundaries of the destination buffer.
 /// </para>
 /// <para>
 /// The destination buffer must be the same format as the source buffer.  If it is not, then an exception will be thrown.
 /// </para>
 /// </remarks>
 public void CopyTo(IGorgonImageBuffer buffer, in DX.Rectangle?sourceRegion = null, int destX = 0, int destY = 0)
Beispiel #18
0
        /// <summary>
        /// Function to convert an individual <see cref="IGorgonImageBuffer"/> to a GDI+ bitmap object.
        /// </summary>
        /// <param name="buffer">The buffer to convert.</param>
        /// <returns>A new GDI+ bitmap object.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="buffer"/> parameter is <b>null</b>.</exception>
        /// <exception cref="GorgonException">Thrown if the <paramref name="buffer"/> is not a 32 bit <c>R8G8B8A8</c> format, or <c>B8G8R8*</c> format.</exception>
        /// <remarks>
        /// <para>
        /// This method will take a <see cref="IGorgonImageBuffer"/> and copy its data into a new 2D <see cref="Bitmap"/>.
        /// </para>
        /// <para>
        /// Some format conversion is performed on the <paramref name="buffer"/> when it is imported. The format conversion will always convert to a pixel format of <c>Format32bppArgb</c> or
        /// <c>Format24bppRgb</c>.  The following formats are supported for 32 bit conversion:
        /// <list type="bullet">
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_UNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_UNorm_SRgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_SInt"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_SNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_UInt"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.R8G8B8A8_Typeless"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8A8_UNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8A8_UNorm_SRgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8A8_Typeless"/></term>
        ///     </item>
        /// </list>
        /// The following formats are supported for 24 bit conversion:
        /// <list type="bullet">
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8X8_UNorm"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8X8_UNorm_SRgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="BufferFormat.B8G8R8X8_Typeless"/></term>
        ///     </item>
        /// </list>
        /// </para>
        /// <para>
        /// If the source <paramref name="buffer"/> does not support any of the formats on the lists, then an exception will be thrown.
        /// </para>
        /// </remarks>
        public static Bitmap ToBitmap(this IGorgonImageBuffer buffer)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            PixelFormat pixelFormat;
            bool        needsSwizzle = false;

            switch (buffer.Format)
            {
            case BufferFormat.R8G8B8A8_UNorm:
            case BufferFormat.R8G8B8A8_UNorm_SRgb:
            case BufferFormat.R8G8B8A8_SInt:
            case BufferFormat.R8G8B8A8_SNorm:
            case BufferFormat.R8G8B8A8_UInt:
            case BufferFormat.R8G8B8A8_Typeless:
                pixelFormat  = PixelFormat.Format32bppArgb;
                needsSwizzle = true;
                break;

            case BufferFormat.B8G8R8A8_UNorm:
            case BufferFormat.B8G8R8A8_Typeless:
            case BufferFormat.B8G8R8A8_UNorm_SRgb:
                pixelFormat = PixelFormat.Format32bppArgb;
                break;

            case BufferFormat.B8G8R8X8_UNorm:
            case BufferFormat.B8G8R8X8_Typeless:
            case BufferFormat.B8G8R8X8_UNorm_SRgb:
                pixelFormat = PixelFormat.Format24bppRgb;
                break;

            default:
                throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, buffer.Format));
            }

            var result = new Bitmap(buffer.Width, buffer.Height, pixelFormat);

            unsafe
            {
                BitmapData destData = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, pixelFormat);

                try
                {
                    byte *srcPtr  = (byte *)buffer.Data;
                    byte *destPtr = (byte *)destData.Scan0;

                    for (int y = 0; y < buffer.Height; ++y)
                    {
                        byte *src  = srcPtr + (y * buffer.PitchInformation.RowPitch);
                        byte *dest = destPtr + (y * destData.Stride);

                        switch (pixelFormat)
                        {
                        case PixelFormat.Format32bppArgb:
                            if (!needsSwizzle)
                            {
                                Unsafe.CopyBlock(dest, src, (uint)(destData.Stride.Min(buffer.PitchInformation.RowPitch)));
                                continue;
                            }

                            ImageUtilities.SwizzleScanline(src, buffer.PitchInformation.RowPitch, dest, destData.Stride, buffer.Format, ImageBitFlags.None);
                            break;

                        case PixelFormat.Format24bppRgb:
                            ImageUtilities.Compress24BPPScanLine(src, buffer.PitchInformation.RowPitch, dest, destData.Stride, true);
                            break;
                        }
                    }
                }
                finally
                {
                    result.UnlockBits(destData);
                }
            }

            return(result);
        }