diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index eaf4e63a0a..af586e2544 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -75,7 +75,7 @@ public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options) /// /// Gets the dimensions of the image. /// - public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height); + public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height); /// public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) @@ -87,7 +87,7 @@ public Image Decode(BufferedReadStream stream, CancellationToken this.currentStream.Skip(this.fileHeader.IdLength); // Parse the color map, if present. - if (this.fileHeader.ColorMapType != 0 && this.fileHeader.ColorMapType != 1) + if (this.fileHeader.ColorMapType is not 0 and not 1) { TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found"); } @@ -117,7 +117,11 @@ public Image Decode(BufferedReadStream stream, CancellationToken using (IMemoryOwner palette = this.memoryAllocator.Allocate(colorMapSizeInBytes, AllocationOptions.Clean)) { Span paletteSpan = palette.GetSpan(); - this.currentStream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes); + int bytesRead = this.currentStream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes); + if (bytesRead != colorMapSizeInBytes) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read the color map"); + } if (this.fileHeader.ImageType == TgaImageType.RleColorMapped) { @@ -308,8 +312,7 @@ private void ReadPaletted(int width, int height, Buffer2D pixels private void ReadPalettedRle(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - int bytesPerPixel = 1; - using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) + using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean)) { TPixel color = default; Span bufferSpan = buffer.GetSpan(); @@ -319,7 +322,7 @@ private void ReadPalettedRle(int width, int height, Buffer2D pix { int newY = InvertY(y, height, origin); Span pixelRow = pixels.DangerousGetRowSpan(newY); - int rowStartIdx = y * width * bytesPerPixel; + int rowStartIdx = y * width; for (int x = 0; x < width; x++) { int idx = rowStartIdx + x; @@ -418,7 +421,12 @@ private void ReadBgra16(int width, int height, Buffer2D pixels, { for (int x = width - 1; x >= 0; x--) { - this.currentStream.Read(this.scratchBuffer, 0, 2); + int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 2); + if (bytesRead != 2) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); + } + if (!this.hasAlpha) { this.scratchBuffer[1] |= 1 << 7; @@ -438,7 +446,11 @@ private void ReadBgra16(int width, int height, Buffer2D pixels, } else { - this.currentStream.Read(rowSpan); + int bytesRead = this.currentStream.Read(rowSpan); + if (bytesRead != rowSpan.Length) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); + } if (!this.hasAlpha) { @@ -579,7 +591,7 @@ private void ReadRle(int width, int height, Buffer2D pixels, int where TPixel : unmanaged, IPixel { TPixel color = default; - var alphaBits = this.tgaMetadata.AlphaChannelBits; + byte alphaBits = this.tgaMetadata.AlphaChannelBits; using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) { Span bufferSpan = buffer.GetSpan(); @@ -624,8 +636,8 @@ private void ReadRle(int width, int height, Buffer2D pixels, int } else { - var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; - color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha)); + byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; + color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha)); } break; @@ -653,7 +665,12 @@ public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancella private void ReadL8Row(int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { - this.currentStream.Read(row); + int bytesRead = this.currentStream.Read(row); + if (bytesRead != row.Length) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); + } + Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromL8Bytes(this.Configuration, row, pixelSpan, width); } @@ -662,7 +679,7 @@ private void ReadL8Row(int width, Buffer2D pixels, Span ro private void ReadL8Pixel(TPixel color, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { - var pixelValue = (byte)this.currentStream.ReadByte(); + byte pixelValue = (byte)this.currentStream.ReadByte(); color.FromL8(Unsafe.As(ref pixelValue)); pixelSpan[x] = color; } @@ -671,7 +688,12 @@ private void ReadL8Pixel(TPixel color, int x, Span pixelSpan) private void ReadBgr24Pixel(TPixel color, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { - this.currentStream.Read(this.scratchBuffer, 0, 3); + int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 3); + if (bytesRead != 3) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel"); + } + color.FromBgr24(Unsafe.As(ref this.scratchBuffer[0])); pixelSpan[x] = color; } @@ -680,7 +702,12 @@ private void ReadBgr24Pixel(TPixel color, int x, Span pixelSpan) private void ReadBgr24Row(int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { - this.currentStream.Read(row); + int bytesRead = this.currentStream.Read(row); + if (bytesRead != row.Length) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); + } + Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromBgr24Bytes(this.Configuration, row, pixelSpan, width); } @@ -689,8 +716,13 @@ private void ReadBgr24Row(int width, Buffer2D pixels, Span private void ReadBgra32Pixel(int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { - this.currentStream.Read(this.scratchBuffer, 0, 4); - var alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3]; + int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 4); + if (bytesRead != 4) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel"); + } + + byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3]; color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha)); pixelRow[x] = color; } @@ -699,7 +731,12 @@ private void ReadBgra32Pixel(int x, TPixel color, Span pixelRow) private void ReadBgra32Row(int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { - this.currentStream.Read(row); + int bytesRead = this.currentStream.Read(row); + if (bytesRead != row.Length) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); + } + Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromBgra32Bytes(this.Configuration, row, pixelSpan, width); } @@ -709,6 +746,11 @@ private void ReadPalettedBgra16Pixel(Span palette, int colorMapPix where TPixel : unmanaged, IPixel { int colorIndex = this.currentStream.ReadByte(); + if (colorIndex == -1) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); + } + this.ReadPalettedBgra16Pixel(palette, colorIndex, colorMapPixelSizeInBytes, ref color); pixelRow[x] = color; } @@ -734,6 +776,11 @@ private void ReadPalettedBgr24Pixel(Span palette, int colorMapPixe where TPixel : unmanaged, IPixel { int colorIndex = this.currentStream.ReadByte(); + if (colorIndex == -1) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); + } + color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); pixelRow[x] = color; } @@ -743,6 +790,11 @@ private void ReadPalettedBgra32Pixel(Span palette, int colorMapPix where TPixel : unmanaged, IPixel { int colorIndex = this.currentStream.ReadByte(); + if (colorIndex == -1) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); + } + color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); pixelRow[x] = color; } @@ -757,7 +809,7 @@ private void ReadPalettedBgra32Pixel(Span palette, int colorMapPix private void UncompressRle(int width, int height, Span buffer, int bytesPerPixel) { int uncompressedPixels = 0; - var pixel = new byte[bytesPerPixel]; + Span pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel); int totalPixels = width * height; while (uncompressedPixels < totalPixels) { @@ -768,11 +820,16 @@ private void UncompressRle(int width, int height, Span buffer, int bytesPe if (highBit == 1) { int runLength = runLengthByte & 127; - this.currentStream.Read(pixel, 0, bytesPerPixel); + int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel); + if (bytesRead != bytesPerPixel) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); + } + int bufferIdx = uncompressedPixels * bytesPerPixel; for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) { - pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + pixel.CopyTo(buffer.Slice(bufferIdx)); bufferIdx += bytesPerPixel; } } @@ -783,8 +840,13 @@ private void UncompressRle(int width, int height, Span buffer, int bytesPe int bufferIdx = uncompressedPixels * bytesPerPixel; for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) { - this.currentStream.Read(pixel, 0, bytesPerPixel); - pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel); + if (bytesRead != bytesPerPixel) + { + TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); + } + + pixel.CopyTo(buffer.Slice(bufferIdx)); bufferIdx += bytesPerPixel; } } @@ -815,17 +877,12 @@ private static int InvertY(int y, int height, TgaImageOrigin origin) /// The image origin. /// True, if y coordinate needs to be inverted. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool InvertY(TgaImageOrigin origin) + private static bool InvertY(TgaImageOrigin origin) => origin switch { - switch (origin) - { - case TgaImageOrigin.BottomLeft: - case TgaImageOrigin.BottomRight: - return true; - default: - return false; - } - } + TgaImageOrigin.BottomLeft => true, + TgaImageOrigin.BottomRight => true, + _ => false + }; /// /// Returns the x- value based on the given width. @@ -851,17 +908,13 @@ private static int InvertX(int x, int width, TgaImageOrigin origin) /// The image origin. /// True, if x coordinate needs to be inverted. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool InvertX(TgaImageOrigin origin) - { - switch (origin) + private static bool InvertX(TgaImageOrigin origin) => + origin switch { - case TgaImageOrigin.TopRight: - case TgaImageOrigin.BottomRight: - return true; - default: - return false; - } - } + TgaImageOrigin.TopRight => true, + TgaImageOrigin.BottomRight => true, + _ => false + }; /// /// Reads the tga file header from the stream. @@ -880,8 +933,8 @@ private TgaImageOrigin ReadFileHeader(BufferedReadStream stream) this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; - var alphaBits = this.fileHeader.ImageDescriptor & 0xf; - if (alphaBits != 0 && alphaBits != 1 && alphaBits != 8) + int alphaBits = this.fileHeader.ImageDescriptor & 0xf; + if (alphaBits is not 0 and not 1 and not 8) { TgaThrowHelper.ThrowInvalidImageContentException("Invalid alpha channel bits"); } diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index 16910040c5..fa0ea6f90b 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -117,7 +117,6 @@ public void Encode(Image image, Stream stream, CancellationToken fileHeader.WriteTo(buffer); stream.Write(buffer, 0, TgaFileHeader.Size); - if (this.compression is TgaCompression.RunLength) { this.WriteRunLengthEncodedImage(stream, image.Frames.RootFrame); @@ -175,69 +174,98 @@ private void WriteRunLengthEncodedImage(Stream stream, ImageFrame pixels = image.PixelBuffer; - int totalPixels = image.Width * image.Height; - int encodedPixels = 0; - while (encodedPixels < totalPixels) + for (int y = 0; y < image.Height; y++) { - int x = encodedPixels % pixels.Width; - int y = encodedPixels / pixels.Width; - TPixel currentPixel = pixels[x, y]; - currentPixel.ToRgba32(ref color); - byte equalPixelCount = this.FindEqualPixels(pixels, x, y); - - // Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run. - stream.WriteByte((byte)(equalPixelCount | 128)); - switch (this.bitsPerPixel) + Span pixelRow = pixels.DangerousGetRowSpan(y); + for (int x = 0; x < image.Width;) { - case TgaBitsPerPixel.Pixel8: - int luminance = GetLuminance(currentPixel); - stream.WriteByte((byte)luminance); - break; - - case TgaBitsPerPixel.Pixel16: - var bgra5551 = new Bgra5551(color.ToVector4()); - BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); - stream.WriteByte(this.buffer[0]); - stream.WriteByte(this.buffer[1]); - - break; - - case TgaBitsPerPixel.Pixel24: - stream.WriteByte(color.B); - stream.WriteByte(color.G); - stream.WriteByte(color.R); - break; - - case TgaBitsPerPixel.Pixel32: - stream.WriteByte(color.B); - stream.WriteByte(color.G); - stream.WriteByte(color.R); - stream.WriteByte(color.A); - break; - default: - break; + TPixel currentPixel = pixelRow[x]; + currentPixel.ToRgba32(ref color); + byte equalPixelCount = this.FindEqualPixels(pixelRow, x); + + if (equalPixelCount > 0) + { + // Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run. + stream.WriteByte((byte)(equalPixelCount | 128)); + this.WritePixel(stream, currentPixel, color); + x += equalPixelCount + 1; + } + else + { + // Write Raw Packet (i.e., Non-Run-Length Encoded): + byte unEqualPixelCount = this.FindUnEqualPixels(pixelRow, x); + stream.WriteByte(unEqualPixelCount); + this.WritePixel(stream, currentPixel, color); + x++; + for (int i = 0; i < unEqualPixelCount; i++) + { + currentPixel = pixelRow[x]; + currentPixel.ToRgba32(ref color); + this.WritePixel(stream, currentPixel, color); + x++; + } + } } + } + } - encodedPixels += equalPixelCount + 1; + /// + /// Writes a the pixel to the stream. + /// + /// The type of the pixel. + /// The stream to write to. + /// The current pixel. + /// The color of the pixel to write. + private void WritePixel(Stream stream, TPixel currentPixel, Rgba32 color) + where TPixel : unmanaged, IPixel + { + switch (this.bitsPerPixel) + { + case TgaBitsPerPixel.Pixel8: + int luminance = GetLuminance(currentPixel); + stream.WriteByte((byte)luminance); + break; + + case TgaBitsPerPixel.Pixel16: + var bgra5551 = new Bgra5551(color.ToVector4()); + BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); + stream.WriteByte(this.buffer[0]); + stream.WriteByte(this.buffer[1]); + + break; + + case TgaBitsPerPixel.Pixel24: + stream.WriteByte(color.B); + stream.WriteByte(color.G); + stream.WriteByte(color.R); + break; + + case TgaBitsPerPixel.Pixel32: + stream.WriteByte(color.B); + stream.WriteByte(color.G); + stream.WriteByte(color.R); + stream.WriteByte(color.A); + break; + default: + break; } } /// - /// Finds consecutive pixels which have the same value. + /// Finds consecutive pixels which have the same value up to 128 pixels maximum. /// /// The pixel type. - /// The pixels of the image. + /// A pixel row of the image to encode. /// X coordinate to start searching for the same pixels. - /// Y coordinate to searching for the same pixels in only one scan line. /// The number of equal pixels. - private byte FindEqualPixels(Buffer2D pixels, int xStart, int yPos) + private byte FindEqualPixels(Span pixelRow, int xStart) where TPixel : unmanaged, IPixel { byte equalPixelCount = 0; - TPixel startPixel = pixels[xStart, yPos]; - for (int x = xStart + 1; x < pixels.Width; x++) + TPixel startPixel = pixelRow[xStart]; + for (int x = xStart + 1; x < pixelRow.Length; x++) { - TPixel nextPixel = pixels[x, yPos]; + TPixel nextPixel = pixelRow[x]; if (startPixel.Equals(nextPixel)) { equalPixelCount++; @@ -256,6 +284,39 @@ private byte FindEqualPixels(Buffer2D pixels, int xStart, int yP return equalPixelCount; } + /// + /// Finds consecutive pixels which are unequal up to 128 pixels maximum. + /// + /// The pixel type. + /// A pixel row of the image to encode. + /// X coordinate to start searching for the unequal pixels. + /// The number of equal pixels. + private byte FindUnEqualPixels(Span pixelRow, int xStart) + where TPixel : unmanaged, IPixel + { + byte unEqualPixelCount = 0; + TPixel currentPixel = pixelRow[xStart]; + for (int x = xStart + 1; x < pixelRow.Length; x++) + { + TPixel nextPixel = pixelRow[x]; + if (currentPixel.Equals(nextPixel)) + { + return unEqualPixelCount; + } + + unEqualPixelCount++; + + if (unEqualPixelCount >= 127) + { + return unEqualPixelCount; + } + + currentPixel = nextPixel; + } + + return unEqualPixelCount; + } + private IMemoryOwner AllocateRow(int width, int bytesPerPixel) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, 0); diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index f3aa38df99..57d8aeff51 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -733,6 +733,20 @@ public void TgaDecoder_CanDecode_WhenAlphaBitsNotSet(TestImageProvider(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + ImageComparingUtils.CompareWithReferenceDecoder(provider, image); + } + } + [Theory] [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index 12e24622a8..abe9e2a2d8 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -56,12 +56,8 @@ public void TgaEncoder_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bm [MemberData(nameof(TgaBitsPerPixelFiles))] public void TgaEncoder_WithCompression_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) { - var options = new TgaEncoder() - { - Compression = TgaCompression.RunLength - }; - - TestFile testFile = TestFile.Create(imagePath); + var options = new TgaEncoder() { Compression = TgaCompression.RunLength }; + var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) @@ -121,6 +117,42 @@ public void TgaEncoder_Bit24_WithRunLengthEncoding_Works(TestImageProvid public void TgaEncoder_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); + [Theory] + [WithFile(WhiteStripesPattern, PixelTypes.Rgba32, 2748)] + public void TgaEncoder_DoesNotAlwaysUseRunLengthPackets(TestImageProvider provider, int expectedBytes) + where TPixel : unmanaged, IPixel + { + // The test image has alternating black and white pixels, which should make using always RLE data inefficient. + using (Image image = provider.GetImage()) + { + var options = new TgaEncoder() { Compression = TgaCompression.RunLength }; + using (var memStream = new MemoryStream()) + { + image.Save(memStream, options); + byte[] imageBytes = memStream.ToArray(); + Assert.Equal(expectedBytes, imageBytes.Length); + } + } + } + + // Run length encoded pixels should not exceed row boundaries. + // https://github.com/SixLabors/ImageSharp/pull/2172 + [Fact] + public void TgaEncoder_RunLengthDoesNotCrossRowBoundaries() + { + var options = new TgaEncoder() { Compression = TgaCompression.RunLength }; + + using (var input = new Image(30, 30)) + { + using (var memStream = new MemoryStream()) + { + input.Save(memStream, options); + byte[] imageBytes = memStream.ToArray(); + Assert.Equal(138, imageBytes.Length); + } + } + } + [Theory] [WithFile(Bit32BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel24)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3efb528a82..306a28dae9 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -544,6 +544,9 @@ public static class Tga public const string NoAlphaBits16BitRle = "Tga/16bit_rle_noalphabits.tga"; public const string NoAlphaBits32Bit = "Tga/32bit_no_alphabits.tga"; public const string NoAlphaBits32BitRle = "Tga/32bit_rle_no_alphabits.tga"; + + public const string Github_RLE_legacy = "Tga/Github_RLE_legacy.tga"; + public const string WhiteStripesPattern = "Tga/whitestripes.png"; } public static class Webp diff --git a/tests/Images/Input/Tga/Github_RLE_legacy.tga b/tests/Images/Input/Tga/Github_RLE_legacy.tga new file mode 100644 index 0000000000..0cb1f73c19 --- /dev/null +++ b/tests/Images/Input/Tga/Github_RLE_legacy.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3570d2883a10a764577dd5174a9168320e8653b220800714da8e3880f752ab5e +size 51253 diff --git a/tests/Images/Input/Tga/whitestripes.png b/tests/Images/Input/Tga/whitestripes.png new file mode 100644 index 0000000000..b7f6c94b48 --- /dev/null +++ b/tests/Images/Input/Tga/whitestripes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bc5d67ce368d2a40fb99df994c6973287fca2d8c8cff78227996f9acb5c6e1e +size 127