Example #1
0
        /// <summary>
        /// Function to write out the DDS header to the stream.
        /// </summary>
        /// <param name="settings">Meta data for the image header.</param>
        /// <param name="conversionFlags">Flags required for image conversion.</param>
        /// <returns>A TGA header value, populated with the correct settings.</returns>
        private TgaHeader GetHeader(IGorgonImageInfo settings, out TGAConversionFlags conversionFlags)
        {
            TgaHeader header = default;

            conversionFlags = TGAConversionFlags.None;

            if ((settings.Width > 0xFFFF) ||
                (settings.Height > 0xFFFF))
            {
                throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec));
            }

            header.Width  = (ushort)(settings.Width);
            header.Height = (ushort)(settings.Height);

            switch (settings.Format)
            {
            case BufferFormat.R8G8B8A8_UNorm:
            case BufferFormat.R8G8B8A8_UNorm_SRgb:
                header.ImageType  = TgaImageType.TrueColor;
                header.BPP        = 32;
                header.Descriptor = TgaDescriptor.InvertY | TgaDescriptor.RGB888A8;
                conversionFlags  |= TGAConversionFlags.Swizzle;
                break;

            case BufferFormat.B8G8R8A8_UNorm:
            case BufferFormat.B8G8R8A8_UNorm_SRgb:
                header.ImageType  = TgaImageType.TrueColor;
                header.BPP        = 32;
                header.Descriptor = TgaDescriptor.InvertY | TgaDescriptor.RGB888A8;
                break;

            case BufferFormat.B8G8R8X8_UNorm:
            case BufferFormat.B8G8R8X8_UNorm_SRgb:
                header.ImageType  = TgaImageType.TrueColor;
                header.BPP        = 24;
                header.Descriptor = TgaDescriptor.InvertY;
                conversionFlags  |= TGAConversionFlags.RGB888;
                break;

            case BufferFormat.R8_UNorm:
            case BufferFormat.A8_UNorm:
                header.ImageType  = TgaImageType.BlackAndWhite;
                header.BPP        = 8;
                header.Descriptor = TgaDescriptor.InvertY;
                break;

            case BufferFormat.B5G5R5A1_UNorm:
                header.ImageType  = TgaImageType.TrueColor;
                header.BPP        = 16;
                header.Descriptor = TgaDescriptor.InvertY | TgaDescriptor.RGB555A1;
                break;

            default:
                throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, settings.Format));
            }

            // Persist to stream.
            return(header);
        }
Example #2
0
        /// <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)
        {
            // Ensure that we can actually read this format.  We do not perform total pixel conversion on behalf of the user, they are responsible for that.
            // We will, however, support swizzling and pixel compression (e.g. 32 -> 24 bit).
            if (Array.IndexOf(_supportedFormats, imageData.Format) == -1)
            {
                throw new NotSupportedException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, imageData.Format));
            }

            using (var writer = new GorgonBinaryWriter(stream, true))
            {
                // Write the header for the file before we dump the file contents.
                TgaHeader header = GetHeader(imageData, out TGAConversionFlags conversionFlags);

                GorgonPitchLayout destPitch;

                if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888)
                {
                    destPitch = new GorgonPitchLayout(imageData.Width * 3, imageData.Width * 3 * imageData.Height);
                }
                else
                {
                    var formatInfo = new GorgonFormatInfo(imageData.Format);
                    destPitch = formatInfo.GetPitchForFormat(imageData.Width, imageData.Height);
                }

                GorgonPitchLayout srcPitch = imageData.Buffers[0].PitchInformation;

                // If the two pitches are equal and we have no conversion requirements, then just write out the buffer.
                if ((destPitch == srcPitch) && (conversionFlags == TGAConversionFlags.None))
                {
                    writer.WriteValue(ref header);
                    writer.WriteRange(imageData.Buffers[0].Data, count: srcPitch.SlicePitch);
                    return;
                }

                unsafe
                {
                    // Get the pointer to the first mip/array/depth level.
                    byte *srcPointer = (byte *)imageData.Buffers[0].Data;
                    var   lineBuffer = new GorgonNativeBuffer <byte>(srcPitch.RowPitch);

                    try
                    {
                        // Persist the working buffer to the stream.
                        writer.WriteValue(ref header);

                        // Write out each scan line.
                        for (int y = 0; y < imageData.Height; y++)
                        {
                            byte *destPtr = (byte *)lineBuffer;

                            if ((conversionFlags & TGAConversionFlags.RGB888) == TGAConversionFlags.RGB888)
                            {
                                ImageUtilities.Compress24BPPScanLine(srcPointer,
                                                                     srcPitch.RowPitch,
                                                                     destPtr,
                                                                     destPitch.RowPitch,
                                                                     (conversionFlags & TGAConversionFlags.Swizzle) == TGAConversionFlags.Swizzle);
                            }
                            else if ((conversionFlags & TGAConversionFlags.Swizzle) == TGAConversionFlags.Swizzle)
                            {
                                ImageUtilities.SwizzleScanline(srcPointer, srcPitch.RowPitch, destPtr, destPitch.RowPitch, imageData.Format, ImageBitFlags.None);
                            }
                            else
                            {
                                ImageUtilities.CopyScanline(srcPointer, srcPitch.RowPitch, destPtr, destPitch.RowPitch, imageData.Format, ImageBitFlags.None);
                            }

                            srcPointer += srcPitch.RowPitch;

                            writer.WriteRange(lineBuffer, count: destPitch.RowPitch);
                        }
                    }
                    finally
                    {
                        lineBuffer?.Dispose();
                    }
                }
            }
        }
Example #3
0
        /// <summary>
        /// Function to read in the TGA header from a stream.
        /// </summary>
        /// <param name="reader">The reader used to read the stream containing the data.</param>
        /// <param name="conversionFlags">Flags for conversion.</param>
        /// <returns>New image settings.</returns>
        private static IGorgonImageInfo ReadHeader(GorgonBinaryReader reader, out TGAConversionFlags conversionFlags)
        {
            conversionFlags = TGAConversionFlags.None;

            // Get the header for the file.
            TgaHeader header = reader.ReadValue <TgaHeader>();

            if ((header.ColorMapType != 0) || (header.ColorMapLength != 0) ||
                (header.Width <= 0) || (header.Height <= 0) ||
                ((header.Descriptor & TgaDescriptor.Interleaved2Way) == TgaDescriptor.Interleaved2Way) ||
                ((header.Descriptor & TgaDescriptor.Interleaved4Way) == TgaDescriptor.Interleaved4Way))
            {
                throw new NotSupportedException(Resources.GORIMG_ERR_TGA_TYPE_NOT_SUPPORTED);
            }

            BufferFormat pixelFormat = BufferFormat.Unknown;

            switch (header.ImageType)
            {
            case TgaImageType.TrueColor:
            case TgaImageType.TrueColorRLE:
                switch (header.BPP)
                {
                case 16:
                    pixelFormat = BufferFormat.B5G5R5A1_UNorm;
                    break;

                case 24:
                case 32:
                    pixelFormat = BufferFormat.R8G8B8A8_UNorm;
                    if (header.BPP == 24)
                    {
                        conversionFlags |= TGAConversionFlags.Expand;
                    }
                    break;
                }

                if (header.ImageType == TgaImageType.TrueColorRLE)
                {
                    conversionFlags |= TGAConversionFlags.RLE;
                }
                break;

            case TgaImageType.BlackAndWhite:
            case TgaImageType.BlackAndWhiteRLE:
                if (header.BPP == 8)
                {
                    pixelFormat = BufferFormat.R8_UNorm;
                }
                else
                {
                    throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, header.ImageType));
                }

                if (header.ImageType == TgaImageType.BlackAndWhiteRLE)
                {
                    conversionFlags |= TGAConversionFlags.RLE;
                }
                break;

            default:
                throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, header.ImageType));
            }

            var settings = new GorgonImageInfo(ImageType.Image2D, pixelFormat)
            {
                MipCount   = 1,
                ArrayCount = 1,
                Width      = header.Width,
                Height     = header.Height
            };

            if ((header.Descriptor & TgaDescriptor.InvertX) == TgaDescriptor.InvertX)
            {
                conversionFlags |= TGAConversionFlags.InvertX;
            }

            if ((header.Descriptor & TgaDescriptor.InvertY) == TgaDescriptor.InvertY)
            {
                conversionFlags |= TGAConversionFlags.InvertY;
            }

            if (header.IDLength <= 0)
            {
                return(settings);
            }

            // Skip these bytes.
            for (int i = 0; i < header.IDLength; i++)
            {
                reader.ReadByte();
            }

            return(settings);
        }