Beispiel #1
0
        private static int InvertX(int x, int width, TgaImageOrigin origin)
        {
            if (InvertX(origin))
            {
                return(width - x - 1);
            }

            return(x);
        }
Beispiel #2
0
        private static int InvertY(int y, int height, TgaImageOrigin origin)
        {
            if (InvertY(origin))
            {
                return(height - y - 1);
            }

            return(y);
        }
Beispiel #3
0
        private static bool InvertX(TgaImageOrigin origin)
        {
            switch (origin)
            {
            case TgaImageOrigin.TopRight:
            case TgaImageOrigin.BottomRight:
                return(true);

            default:
                return(false);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Decodes the image from the specified stream.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
        /// <exception cref="System.ArgumentNullException">
        ///    <para><paramref name="stream"/> is null.</para>
        /// </exception>
        /// <returns>The decoded image.</returns>
        public Image <TPixel> Decode <TPixel>(Stream stream)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            try
            {
                TgaImageOrigin origin = this.ReadFileHeader(stream);
                this.currentStream.Skip(this.fileHeader.IdLength);

                // Parse the color map, if present.
                if (this.fileHeader.ColorMapType != 0 && this.fileHeader.ColorMapType != 1)
                {
                    TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found");
                }

                if (this.fileHeader.Width == 0 || this.fileHeader.Height == 0)
                {
                    throw new UnknownImageFormatException("Width or height cannot be 0");
                }

                var image = Image.CreateUninitialized <TPixel>(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
                Buffer2D <TPixel> pixels = image.GetRootFramePixelBuffer();

                if (this.fileHeader.ColorMapType == 1)
                {
                    if (this.fileHeader.CMapLength <= 0)
                    {
                        TgaThrowHelper.ThrowImageFormatException("Missing tga color map length");
                    }

                    if (this.fileHeader.CMapDepth <= 0)
                    {
                        TgaThrowHelper.ThrowImageFormatException("Missing tga color map depth");
                    }

                    int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
                    int colorMapSizeInBytes      = this.fileHeader.CMapLength * colorMapPixelSizeInBytes;
                    using (IManagedByteBuffer palette = this.memoryAllocator.AllocateManagedByteBuffer(colorMapSizeInBytes, AllocationOptions.Clean))
                    {
                        this.currentStream.Read(palette.Array, this.fileHeader.CMapStart, colorMapSizeInBytes);

                        if (this.fileHeader.ImageType == TgaImageType.RleColorMapped)
                        {
                            this.ReadPalettedRle(
                                this.fileHeader.Width,
                                this.fileHeader.Height,
                                pixels,
                                palette.Array,
                                colorMapPixelSizeInBytes,
                                origin);
                        }
                        else
                        {
                            this.ReadPaletted(
                                this.fileHeader.Width,
                                this.fileHeader.Height,
                                pixels,
                                palette.Array,
                                colorMapPixelSizeInBytes,
                                origin);
                        }
                    }

                    return(image);
                }

                // Even if the image type indicates it is not a paletted image, it can still contain a palette. Skip those bytes.
                if (this.fileHeader.CMapLength > 0)
                {
                    int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
                    this.currentStream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes);
                }

                switch (this.fileHeader.PixelDepth)
                {
                case 8:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin);
                    }
                    else
                    {
                        this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
                    }

                    break;

                case 15:
                case 16:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin);
                    }
                    else
                    {
                        this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
                    }

                    break;

                case 24:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin);
                    }
                    else
                    {
                        this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
                    }

                    break;

                case 32:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin);
                    }
                    else
                    {
                        this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
                    }

                    break;

                default:
                    TgaThrowHelper.ThrowNotSupportedException("ImageSharp does not support this kind of tga files.");
                    break;
                }

                return(image);
            }
            catch (IndexOutOfRangeException e)
            {
                throw new ImageFormatException("TGA image does not have a valid format.", e);
            }
        }
Beispiel #5
0
        /// <summary>
        /// Reads a run length encoded TGA image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="bytesPerPixel">The bytes per pixel.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadRle <TPixel>(int width, int height, Buffer2D <TPixel> pixels, int bytesPerPixel, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color     = default;
            var    alphaBits = this.tgaMetadata.AlphaChannelBits;

            using (IMemoryOwner <byte> buffer = this.memoryAllocator.Allocate <byte>(width * height * bytesPerPixel, AllocationOptions.Clean))
            {
                Span <byte> bufferSpan = buffer.GetSpan();
                this.UncompressRle(width, height, bufferSpan, bytesPerPixel);
                for (int y = 0; y < height; y++)
                {
                    int           newY        = InvertY(y, height, origin);
                    Span <TPixel> pixelRow    = pixels.GetRowSpan(newY);
                    int           rowStartIdx = y * width * bytesPerPixel;
                    for (int x = 0; x < width; x++)
                    {
                        int idx = rowStartIdx + (x * bytesPerPixel);
                        switch (bytesPerPixel)
                        {
                        case 1:
                            color.FromL8(Unsafe.As <byte, L8>(ref bufferSpan[idx]));
                            break;

                        case 2:
                            if (!this.hasAlpha)
                            {
                                // Set alpha value to 1, to treat it as opaque for Bgra5551.
                                bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128);
                            }

                            if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite)
                            {
                                color.FromLa16(Unsafe.As <byte, La16>(ref bufferSpan[idx]));
                            }
                            else
                            {
                                color.FromBgra5551(Unsafe.As <byte, Bgra5551>(ref bufferSpan[idx]));
                            }

                            break;

                        case 3:
                            color.FromBgr24(Unsafe.As <byte, Bgr24>(ref bufferSpan[idx]));
                            break;

                        case 4:
                            if (this.hasAlpha)
                            {
                                color.FromBgra32(Unsafe.As <byte, Bgra32>(ref bufferSpan[idx]));
                            }
                            else
                            {
                                var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
                                color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha));
                            }

                            break;
                        }

                        int newX = InvertX(x, width, origin);
                        pixelRow[newX] = color;
                    }
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Reads a uncompressed TGA image where each pixels has 24 bit.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadBgr24 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            bool invertX = InvertX(origin);

            if (invertX)
            {
                TPixel color = default;
                for (int y = 0; y < height; y++)
                {
                    int           newY      = InvertY(y, height, origin);
                    Span <TPixel> pixelSpan = pixels.GetRowSpan(newY);
                    for (int x = width - 1; x >= 0; x--)
                    {
                        this.ReadBgr24Pixel(color, x, pixelSpan);
                    }
                }

                return;
            }

            using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0))
            {
                bool invertY = InvertY(origin);

                if (invertY)
                {
                    for (int y = height - 1; y >= 0; y--)
                    {
                        this.ReadBgr24Row(width, pixels, row, y);
                    }
                }
                else
                {
                    for (int y = 0; y < height; y++)
                    {
                        this.ReadBgr24Row(width, pixels, row, y);
                    }
                }
            }
        }
Beispiel #7
0
        /// <summary>
        /// Reads a uncompressed TGA image where each pixels has 32 bit.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadBgra32 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color   = default;
            bool   invertX = InvertX(origin);

            if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX)
            {
                using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0))
                {
                    if (InvertY(origin))
                    {
                        for (int y = height - 1; y >= 0; y--)
                        {
                            this.ReadBgra32Row(width, pixels, row, y);
                        }
                    }
                    else
                    {
                        for (int y = 0; y < height; y++)
                        {
                            this.ReadBgra32Row(width, pixels, row, y);
                        }
                    }
                }

                return;
            }

            for (int y = 0; y < height; y++)
            {
                int           newY     = InvertY(y, height, origin);
                Span <TPixel> pixelRow = pixels.GetRowSpan(newY);
                if (invertX)
                {
                    for (int x = width - 1; x >= 0; x--)
                    {
                        this.ReadBgra32Pixel(x, color, pixelRow);
                    }
                }
                else
                {
                    for (int x = 0; x < width; x++)
                    {
                        this.ReadBgra32Pixel(x, color, pixelRow);
                    }
                }
            }
        }
Beispiel #8
0
        /// <summary>
        /// Reads a uncompressed TGA image where each pixels has 16 bit.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadBgra16 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color   = default;
            bool   invertX = InvertX(origin);

            using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0))
            {
                for (int y = 0; y < height; y++)
                {
                    int           newY      = InvertY(y, height, origin);
                    Span <TPixel> pixelSpan = pixels.GetRowSpan(newY);

                    if (invertX)
                    {
                        for (int x = width - 1; x >= 0; x--)
                        {
                            this.currentStream.Read(this.scratchBuffer, 0, 2);
                            if (!this.hasAlpha)
                            {
                                this.scratchBuffer[1] |= 1 << 7;
                            }

                            if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
                            {
                                color.FromLa16(Unsafe.As <byte, La16>(ref this.scratchBuffer[0]));
                            }
                            else
                            {
                                color.FromBgra5551(Unsafe.As <byte, Bgra5551>(ref this.scratchBuffer[0]));
                            }

                            pixelSpan[x] = color;
                        }
                    }
                    else
                    {
                        this.currentStream.Read(row);
                        Span <byte> rowSpan = row.GetSpan();

                        if (!this.hasAlpha)
                        {
                            // We need to set the alpha component value to fully opaque.
                            for (int x = 1; x < rowSpan.Length; x += 2)
                            {
                                rowSpan[x] |= 1 << 7;
                            }
                        }

                        if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
                        {
                            PixelOperations <TPixel> .Instance.FromLa16Bytes(this.configuration, rowSpan, pixelSpan, width);
                        }
                        else
                        {
                            PixelOperations <TPixel> .Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width);
                        }
                    }
                }
            }
        }
Beispiel #9
0
        /// <summary>
        /// Reads a run length encoded TGA image with a palette.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="palette">The color palette.</param>
        /// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadPalettedRle <TPixel>(int width, int height, Buffer2D <TPixel> pixels, byte[] palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int bytesPerPixel = 1;

            using (IMemoryOwner <byte> buffer = this.memoryAllocator.Allocate <byte>(width * height * bytesPerPixel, AllocationOptions.Clean))
            {
                TPixel      color      = default;
                Span <byte> bufferSpan = buffer.GetSpan();
                this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1);

                for (int y = 0; y < height; y++)
                {
                    int           newY        = InvertY(y, height, origin);
                    Span <TPixel> pixelRow    = pixels.GetRowSpan(newY);
                    int           rowStartIdx = y * width * bytesPerPixel;
                    for (int x = 0; x < width; x++)
                    {
                        int idx = rowStartIdx + x;
                        switch (colorMapPixelSizeInBytes)
                        {
                        case 1:
                            color.FromL8(Unsafe.As <byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            break;

                        case 2:
                            this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color);
                            break;

                        case 3:
                            color.FromBgr24(Unsafe.As <byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            break;

                        case 4:
                            color.FromBgra32(Unsafe.As <byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            break;
                        }

                        int newX = InvertX(x, width, origin);
                        pixelRow[newX] = color;
                    }
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// Reads a uncompressed TGA image with a palette.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="palette">The color palette.</param>
        /// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadPaletted <TPixel>(int width, int height, Buffer2D <TPixel> pixels, byte[] palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color   = default;
            bool   invertX = InvertX(origin);

            for (int y = 0; y < height; y++)
            {
                int           newY     = InvertY(y, height, origin);
                Span <TPixel> pixelRow = pixels.GetRowSpan(newY);

                switch (colorMapPixelSizeInBytes)
                {
                case 2:
                    if (invertX)
                    {
                        for (int x = width - 1; x >= 0; x--)
                        {
                            this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }

                    break;

                case 3:
                    if (invertX)
                    {
                        for (int x = width - 1; x >= 0; x--)
                        {
                            this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }

                    break;

                case 4:
                    if (invertX)
                    {
                        for (int x = width - 1; x >= 0; x--)
                        {
                            this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }

                    break;
                }
            }
        }
Beispiel #11
0
        /// <summary>
        /// Reads a uncompressed monochrome TGA image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="origin">the image origin.</param>
        private void ReadMonoChrome <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            bool invertX = InvertX(origin);

            if (invertX)
            {
                TPixel color = default;
                for (int y = 0; y < height; y++)
                {
                    int           newY      = InvertY(y, height, origin);
                    Span <TPixel> pixelSpan = pixels.GetRowSpan(newY);
                    for (int x = width - 1; x >= 0; x--)
                    {
                        this.ReadL8Pixel(color, x, pixelSpan);
                    }
                }

                return;
            }

            using IMemoryOwner <byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0);
            Span <byte> rowSpan           = row.GetSpan();
            bool        invertY           = InvertY(origin);

            if (invertY)
            {
                for (int y = height - 1; y >= 0; y--)
                {
                    this.ReadL8Row(width, pixels, rowSpan, y);
                }
            }
            else
            {
                for (int y = 0; y < height; y++)
                {
                    this.ReadL8Row(width, pixels, rowSpan, y);
                }
            }
        }
Beispiel #12
0
        /// <summary>
        /// Reads a run length encoded TGA image with a palette.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="palette">The color palette.</param>
        /// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadPalettedRle <TPixel>(int width, int height, Buffer2D <TPixel> pixels, byte[] palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int bytesPerPixel = 1;

            using (IMemoryOwner <byte> buffer = this.memoryAllocator.Allocate <byte>(width * height * bytesPerPixel, AllocationOptions.Clean))
            {
                TPixel      color      = default;
                var         alphaBits  = this.tgaMetadata.AlphaChannelBits;
                Span <byte> bufferSpan = buffer.GetSpan();
                this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1);

                for (int y = 0; y < height; y++)
                {
                    int           newY        = InvertY(y, height, origin);
                    Span <TPixel> pixelRow    = pixels.GetRowSpan(newY);
                    int           rowStartIdx = y * width * bytesPerPixel;
                    for (int x = 0; x < width; x++)
                    {
                        int idx = rowStartIdx + x;
                        switch (colorMapPixelSizeInBytes)
                        {
                        case 1:
                            color.FromL8(Unsafe.As <byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            break;

                        case 2:

                            Bgra5551 bgra = Unsafe.As <byte, Bgra5551>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]);
                            if (!this.hasAlpha)
                            {
                                // Set alpha value to 1, to treat it as opaque for Bgra5551.
                                bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000);
                            }

                            color.FromBgra5551(bgra);
                            break;

                        case 3:
                            color.FromBgr24(Unsafe.As <byte, Bgr24>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            break;

                        case 4:
                            if (this.hasAlpha)
                            {
                                color.FromBgra32(Unsafe.As <byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            }
                            else
                            {
                                var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
                                color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha));
                            }

                            break;
                        }

                        int newX = InvertX(x, width, origin);
                        pixelRow[newX] = color;
                    }
                }
            }
        }