From f69048fdcc6faca3b90d3b349e7ce2e857f6160c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 8 Oct 2019 19:32:17 +0200 Subject: [PATCH] Add decoding of 32 bit RLE --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 100 ++++++++++++++----- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 692876cea3..0627be98ca 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -94,7 +94,15 @@ namespace SixLabors.ImageSharp.Formats.Tga break; case 32: - this.ReadBgra32(pixels); + if (this.fileHeader.ImageType.IsRunLengthEncoded()) + { + this.ReadRle32(pixels, this.fileHeader.Width, this.fileHeader.Height); + } + else + { + this.ReadBgra32(pixels); + } + break; default: @@ -196,7 +204,7 @@ namespace SixLabors.ImageSharp.Formats.Tga using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * 3, AllocationOptions.Clean)) { Span bufferSpan = buffer.GetSpan(); - this.UncompressRle24(width, bufferSpan); + this.UncompressRle24(width, height, bufferSpan); for (int y = 0; y < height; y++) { Span pixelRow = pixels.GetRowSpan(this.fileHeader.Height - y - 1); @@ -210,54 +218,38 @@ namespace SixLabors.ImageSharp.Formats.Tga } } - private void UncompressRle24(int w, Span buffer) + private void UncompressRle24(int width, int height, Span buffer) { int uncompressedPixels = 0; -#if NETCOREAPP2_1 - Span pixel = stackalloc byte[3]; -#else var pixel = new byte[3]; -#endif - int totalPixels = this.fileHeader.Height * this.fileHeader.Width; + int totalPixels = width * height; while (uncompressedPixels < totalPixels) { byte runLengthByte = (byte)this.currentStream.ReadByte(); - // The high bit of the run length value is always 1, to indicate that this is a run-length encoded packet. + // The high bit of a run length packet is set to 1. int highBit = runLengthByte >> 7; if (highBit == 1) { int runLength = runLengthByte & 127; - if (runLength == 0) - { - // TgaThrowHelper.ThrowImageFormatException("invalid run length of zero"); - } - this.currentStream.Read(pixel, 0, 3); int bufferIdx = uncompressedPixels * 3; for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) { - buffer[bufferIdx++] = pixel[0]; - buffer[bufferIdx++] = pixel[1]; - buffer[bufferIdx++] = pixel[2]; + pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + bufferIdx += 3; } } else { // Non-run-length encoded packet. int runLength = runLengthByte; - if (runLength == 0) - { - // TgaThrowHelper.ThrowImageFormatException("invalid run length of zero"); - } - int bufferIdx = uncompressedPixels * 3; for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) { this.currentStream.Read(pixel, 0, 3); - buffer[bufferIdx++] = pixel[0]; - buffer[bufferIdx++] = pixel[1]; - buffer[bufferIdx++] = pixel[2]; + pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + bufferIdx += 3; } } } @@ -281,6 +273,64 @@ namespace SixLabors.ImageSharp.Formats.Tga } } + private void ReadRle32(Buffer2D pixels, int width, int height) + where TPixel : struct, IPixel + { + TPixel color = default; + using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * 4, AllocationOptions.Clean)) + { + Span bufferSpan = buffer.GetSpan(); + this.UncompressRle32(width, height, bufferSpan); + for (int y = 0; y < height; y++) + { + Span pixelRow = pixels.GetRowSpan(this.fileHeader.Height - y - 1); + for (int x = 0; x < width; x++) + { + int idx = (y * width * 4) + (x * 4); + color.FromBgra32(Unsafe.As(ref bufferSpan[idx])); + pixelRow[x] = color; + } + } + } + } + + private void UncompressRle32(int width, int height, Span buffer) + { + int uncompressedPixels = 0; + var pixel = new byte[4]; + int totalPixels = width * height; + while (uncompressedPixels < totalPixels) + { + byte runLengthByte = (byte)this.currentStream.ReadByte(); + + // The high bit of a run length packet is set to 1. + int highBit = runLengthByte >> 7; + if (highBit == 1) + { + int runLength = runLengthByte & 127; + this.currentStream.Read(pixel, 0, 4); + int bufferIdx = uncompressedPixels * 4; + for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) + { + pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + bufferIdx += 4; + } + } + else + { + // Non-run-length encoded packet. + int runLength = runLengthByte; + int bufferIdx = uncompressedPixels * 4; + for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) + { + this.currentStream.Read(pixel, 0, 4); + pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + bufferIdx += 4; + } + } + } + } + /// /// Reads the raw image information from the specified stream. ///