diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index e9e7ce7190..a4e9bdcd75 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -237,59 +237,69 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadPaletted(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - using (IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(width, AllocationOptions.Clean)) + TPixel color = default; + bool invertX = InvertX(origin); + + for (int y = 0; y < height; y++) { - TPixel color = default; - Span rowSpan = row.GetSpan(); + int newY = InvertY(y, height, origin); + Span pixelRow = pixels.GetRowSpan(newY); - for (int y = 0; y < height; y++) + switch (colorMapPixelSizeInBytes) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelRow = pixels.GetRowSpan(newY); - switch (colorMapPixelSizeInBytes) - { - case 2: + case 2: + if (invertX) + { + for (int x = width - 1; x >= 0; x--) + { + this.ReadPalettedBgr16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + } + } + else + { for (int x = 0; x < width; x++) { - int colorIndex = rowSpan[x]; - - Bgra5551 bgra = Unsafe.As(ref palette[colorIndex * 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); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadPalettedBgr16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } + } - break; + break; - case 3: + 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++) { - int colorIndex = rowSpan[x]; - color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } + } - break; + break; - case 4: + 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++) { - int colorIndex = rowSpan[x]; - color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); } + } - break; - } + break; } } } @@ -374,20 +384,17 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadMonoChrome(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; - if (isXInverted) + bool invertX = InvertX(origin); + if (invertX) { TPixel color = default; for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); - for (int x = 0; x < width; x++) + for (int x = width - 1; x >= 0; x--) { - var pixelValue = (byte)this.currentStream.ReadByte(); - color.FromL8(Unsafe.As(ref pixelValue)); - int newX = InvertX(x, width, origin); - pixelSpan[newX] = color; + this.ReadL8Pixel(color, x, pixelSpan); } } @@ -396,12 +403,20 @@ namespace SixLabors.ImageSharp.Formats.Tga using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0)) { - for (int y = 0; y < height; y++) + bool invertY = InvertY(origin); + if (invertY) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + for (int y = height - 1; y >= 0; y--) + { + this.ReadL8Row(width, pixels, row, y); + } + } + else + { + for (int y = 0; y < height; y++) + { + this.ReadL8Row(width, pixels, row, y); + } } } } @@ -418,7 +433,7 @@ namespace SixLabors.ImageSharp.Formats.Tga where TPixel : unmanaged, IPixel { TPixel color = default; - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; + bool invertX = InvertX(origin); using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0)) { for (int y = 0; y < height; y++) @@ -426,9 +441,9 @@ namespace SixLabors.ImageSharp.Formats.Tga int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); - if (isXInverted) + if (invertX) { - for (int x = 0; x < width; x++) + for (int x = width - 1; x >= 0; x--) { this.currentStream.Read(this.scratchBuffer, 0, 2); if (!this.hasAlpha) @@ -445,8 +460,7 @@ namespace SixLabors.ImageSharp.Formats.Tga color.FromBgra5551(Unsafe.As(ref this.scratchBuffer[0])); } - int newX = InvertX(x, width, origin); - pixelSpan[newX] = color; + pixelSpan[x] = color; } } else @@ -487,20 +501,17 @@ namespace SixLabors.ImageSharp.Formats.Tga private void ReadBgr24(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; - if (isXInverted) + bool invertX = InvertX(origin); + if (invertX) { TPixel color = default; for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span pixelSpan = pixels.GetRowSpan(newY); - for (int x = 0; x < width; x++) + for (int x = width - 1; x >= 0; x--) { - this.currentStream.Read(this.scratchBuffer, 0, 3); - color.FromBgr24(Unsafe.As(ref this.scratchBuffer[0])); - int newX = InvertX(x, width, origin); - pixelSpan[newX] = color; + this.ReadBgr24Pixel(color, x, pixelSpan); } } @@ -509,12 +520,21 @@ namespace SixLabors.ImageSharp.Formats.Tga using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0)) { - for (int y = 0; y < height; y++) + bool invertY = InvertY(origin); + + if (invertY) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + 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); + } } } } @@ -531,41 +551,46 @@ namespace SixLabors.ImageSharp.Formats.Tga where TPixel : unmanaged, IPixel { TPixel color = default; - bool isXInverted = origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight; - if (this.tgaMetadata.AlphaChannelBits == 8 && !isXInverted) + bool invertX = InvertX(origin); + if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX) { using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { - for (int y = 0; y < height; y++) + if (InvertY(origin)) { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelSpan = pixels.GetRowSpan(newY); - - PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + 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; } - var alphaBits = this.tgaMetadata.AlphaChannelBits; - using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) + for (int y = 0; y < height; y++) { - for (int y = 0; y < height; y++) + int newY = InvertY(y, height, origin); + Span pixelRow = pixels.GetRowSpan(newY); + if (invertX) + { + for (int x = width - 1; x >= 0; x--) + { + this.ReadBgra32Pixel(x, color, pixelRow); + } + } + else { - this.currentStream.Read(row); - int newY = InvertY(y, height, origin); - Span pixelRow = pixels.GetRowSpan(newY); - Span rowSpan = row.GetSpan(); - for (int x = 0; x < width; x++) { - int idx = x * 4; - var alpha = alphaBits == 0 ? byte.MaxValue : rowSpan[idx + 3]; - color.FromBgra32(new Bgra32(rowSpan[idx + 2], rowSpan[idx + 1], rowSpan[idx], (byte)alpha)); - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + this.ReadBgra32Pixel(x, color, pixelRow); } } } @@ -657,6 +682,95 @@ namespace SixLabors.ImageSharp.Formats.Tga this.metadata); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadL8Row(int width, Buffer2D pixels, IManagedByteBuffer row, int y) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(row); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadL8Pixel(TPixel color, int x, Span pixelSpan) + where TPixel : unmanaged, IPixel + { + var pixelValue = (byte)this.currentStream.ReadByte(); + color.FromL8(Unsafe.As(ref pixelValue)); + pixelSpan[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadBgr24Pixel(TPixel color, int x, Span pixelSpan) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(this.scratchBuffer, 0, 3); + color.FromBgr24(Unsafe.As(ref this.scratchBuffer[0])); + pixelSpan[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadBgr24Row(int width, Buffer2D pixels, IManagedByteBuffer row, int y) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(row); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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]; + color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha)); + pixelRow[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadBgra32Row(int width, Buffer2D pixels, IManagedByteBuffer row, int y) + where TPixel : unmanaged, IPixel + { + this.currentStream.Read(row); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + } + + private void ReadPalettedBgr16Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + where TPixel : unmanaged, IPixel + { + int colorIndex = this.currentStream.ReadByte(); + Bgra5551 bgra = default; + bgra.FromBgra5551(Unsafe.As(ref palette[colorIndex * 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); + pixelRow[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadPalettedBgr24Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + where TPixel : unmanaged, IPixel + { + int colorIndex = this.currentStream.ReadByte(); + color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + pixelRow[x] = color; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadPalettedBgra32Pixel(byte[] palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + where TPixel : unmanaged, IPixel + { + int colorIndex = this.currentStream.ReadByte(); + color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + pixelRow[x] = color; + } + /// /// Produce uncompressed tga data from a run length encoded stream. /// @@ -710,14 +824,30 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The representing the inverted value. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int InvertY(int y, int height, TgaImageOrigin origin) + { + if (InvertY(origin)) + { + return height - y - 1; + } + + return y; + } + + /// + /// Indicates whether the y coordinates needs to be inverted, to keep a top left origin. + /// + /// The image origin. + /// True, if y coordinate needs to be inverted. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool InvertY(TgaImageOrigin origin) { switch (origin) { case TgaImageOrigin.BottomLeft: case TgaImageOrigin.BottomRight: - return height - y - 1; + return true; default: - return y; + return false; } } @@ -730,14 +860,30 @@ namespace SixLabors.ImageSharp.Formats.Tga /// The representing the inverted value. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int InvertX(int x, int width, TgaImageOrigin origin) + { + if (InvertX(origin)) + { + return width - x - 1; + } + + return x; + } + + /// + /// Indicates whether the x coordinates needs to be inverted, to keep a top left origin. + /// + /// The image origin. + /// True, if x coordinate needs to be inverted. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool InvertX(TgaImageOrigin origin) { switch (origin) { case TgaImageOrigin.TopRight: case TgaImageOrigin.BottomRight: - return width - x - 1; + return true; default: - return x; + return false; } } diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index ccd00b49f5..f932f994dc 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -20,8 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga private static TgaDecoder TgaDecoder => new TgaDecoder(); [Theory] - [WithFile(Gray8Bit, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_Gray_8Bit(TestImageProvider provider) + [WithFile(Gray8BitTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Gray_WithTopLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -68,8 +68,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Gray8BitRle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_8Bit(TestImageProvider provider) + [WithFile(Gray8BitRleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopLeftOrigin_8Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -80,7 +80,43 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Gray16Bit, PixelTypes.Rgba32)] + [WithFile(Gray8BitRleTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithTopRightOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray8BitRleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomLeftOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray8BitRleBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_WithBottomRightOrigin_8Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Gray16BitTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -140,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Gray16BitRle, PixelTypes.Rgba32)] + [WithFile(Gray16BitRleTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_RunLengthEncoded_Gray_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -248,8 +284,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit24, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_24Bit(TestImageProvider provider) + [WithFile(Bit24TopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithTopLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -344,8 +380,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_32Bit(TestImageProvider provider) + [WithFile(Bit32TopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithTopLeftOrigin_32Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32TopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -380,8 +428,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32TopRight, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_WithTopRightOrigin_32Bit(TestImageProvider provider) + [WithFile(Bit16RleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_16Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -392,8 +440,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit16Rle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_16Bit(TestImageProvider provider) + [WithFile(Bit24RleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -404,8 +452,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit24Rle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvider provider) + [WithFile(Bit32RleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -416,8 +464,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32Rle, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_RunLengthEncoded_32Bit(TestImageProvider provider) + [WithFile(Bit32RleBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithBottomLeftOrigin_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -452,8 +500,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit16Pal, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_WithPalette_16Bit(TestImageProvider provider) + [WithFile(Bit16PalBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_16Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24PalTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteTopLeftOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24PalTopRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteTopRightOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -464,8 +536,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit24Pal, PixelTypes.Rgba32)] - public void TgaDecoder_CanDecode_WithPalette_24Bit(TestImageProvider provider) + [WithFile(Bit24PalBottomLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteBottomLeftOrigin_24Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { using (Image image = provider.GetImage(TgaDecoder)) @@ -476,7 +548,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32Pal, PixelTypes.Rgba32)] + [WithFile(Bit24PalBottomRight, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPaletteBottomRightOrigin_24Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(TgaDecoder)) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32PalTopLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithPalette_32Bit(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -554,7 +638,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit16BottomLeft, PixelTypes.Rgba32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaDecoder_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatException(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -565,7 +649,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga [Theory] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32)] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaDecoder_CanDecode_WithLimitedAllocatorBufferCapacity(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs index 4e3e2d24c3..6e0fa4a0ea 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -25,10 +25,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga public static readonly TheoryData TgaBitsPerPixelFiles = new TheoryData { - { Gray8Bit, TgaBitsPerPixel.Pixel8 }, + { Gray8BitBottomLeft, TgaBitsPerPixel.Pixel8 }, { Bit16BottomLeft, TgaBitsPerPixel.Pixel16 }, { Bit24BottomLeft, TgaBitsPerPixel.Pixel24 }, - { Bit32, TgaBitsPerPixel.Pixel32 }, + { Bit32BottomLeft, TgaBitsPerPixel.Pixel32 }, }; [Theory] @@ -79,51 +79,51 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga } [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit8_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false, compareTolerance: 0.03f); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit16_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit24_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit32_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit8_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) // Using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false, compareTolerance: 0.03f); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit16_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit24_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32)] public void TgaEncoder_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) where TPixel : unmanaged, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); [Theory] - [WithFile(Bit32, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] + [WithFile(Bit32BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel32)] [WithFile(Bit24BottomLeft, PixelTypes.Rgba32, TgaBitsPerPixel.Pixel24)] public void TgaEncoder_WorksWithDiscontiguousBuffers(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 38ef8d9e2c..272998a896 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -375,53 +375,62 @@ namespace SixLabors.ImageSharp.Tests public static class Tga { - public const string Gray8Bit = "Tga/targa_8bit.tga"; - public const string Gray8BitBottomLeft = "Tga/grayscale_LL.tga"; + public const string Gray8BitTopLeft = "Tga/grayscale_UL.tga"; public const string Gray8BitTopRight = "Tga/grayscale_UR.tga"; - public const string Gray8BitBottomRight = "Tga/grayscale_UR.tga"; - public const string Gray8BitRle = "Tga/targa_8bit_rle.tga"; + public const string Gray8BitBottomLeft = "Tga/targa_8bit.tga"; + public const string Gray8BitBottomRight = "Tga/grayscale_LR.tga"; + + public const string Gray8BitRleTopLeft = "Tga/grayscale_rle_UL.tga"; + public const string Gray8BitRleTopRight = "Tga/grayscale_rle_UR.tga"; + public const string Gray8BitRleBottomLeft = "Tga/targa_8bit_rle.tga"; + public const string Gray8BitRleBottomRight = "Tga/grayscale_rle_LR.tga"; public const string Bit15 = "Tga/rgb15.tga"; public const string Bit15Rle = "Tga/rgb15rle.tga"; public const string Bit16BottomLeft = "Tga/targa_16bit.tga"; public const string Bit16PalRle = "Tga/ccm8.tga"; + public const string Bit16RleBottomLeft = "Tga/targa_16bit_rle.tga"; + public const string Bit16PalBottomLeft = "Tga/targa_16bit_pal.tga"; - public const string Gray16Bit = "Tga/grayscale_a_UL.tga"; + public const string Gray16BitTopLeft = "Tga/grayscale_a_UL.tga"; public const string Gray16BitBottomLeft = "Tga/grayscale_a_LL.tga"; public const string Gray16BitBottomRight = "Tga/grayscale_a_LR.tga"; public const string Gray16BitTopRight = "Tga/grayscale_a_UR.tga"; - public const string Gray16BitRle = "Tga/grayscale_a_rle_UL.tga"; + + public const string Gray16BitRleTopLeft = "Tga/grayscale_a_rle_UL.tga"; public const string Gray16BitRleBottomLeft = "Tga/grayscale_a_rle_LL.tga"; public const string Gray16BitRleBottomRight = "Tga/grayscale_a_rle_LR.tga"; public const string Gray16BitRleTopRight = "Tga/grayscale_a_rle_UR.tga"; - public const string Bit24 = "Tga/rgb24_top_left.tga"; + public const string Bit24TopLeft = "Tga/rgb24_top_left.tga"; public const string Bit24BottomLeft = "Tga/targa_24bit.tga"; public const string Bit24BottomRight = "Tga/rgb_LR.tga"; public const string Bit24TopRight = "Tga/rgb_UR.tga"; - public const string Bit24TopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; public const string Bit24RleTopLeft = "Tga/targa_24bit_rle_origin_topleft.tga"; + public const string Bit24RleBottomLeft = "Tga/targa_24bit_rle.tga"; public const string Bit24RleTopRight = "Tga/rgb_rle_UR.tga"; public const string Bit24RleBottomRight = "Tga/rgb_rle_LR.tga"; - public const string Bit32 = "Tga/targa_32bit.tga"; - public const string Bit32BottomLeft = "Tga/rgb_a_LL.tga"; + public const string Bit24PalTopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; + public const string Bit24PalTopRight = "Tga/indexed_UR.tga"; + public const string Bit24PalBottomLeft = "Tga/targa_24bit_pal.tga"; + public const string Bit24PalBottomRight = "Tga/indexed_LR.tga"; + + public const string Bit32TopLeft = "Tga/rgb_a_UL.tga"; + public const string Bit32BottomLeft = "Tga/targa_32bit.tga"; public const string Bit32TopRight = "Tga/rgb_a_UR.tga"; public const string Bit32BottomRight = "Tga/rgb_a_LR.tga"; - public const string Bit32Pal = "Tga/indexed_a_UL.tga"; + public const string Bit32PalTopLeft = "Tga/indexed_a_UL.tga"; public const string Bit32PalBottomLeft = "Tga/indexed_a_LL.tga"; public const string Bit32PalBottomRight = "Tga/indexed_a_LR.tga"; public const string Bit32PalTopRight = "Tga/indexed_a_UR.tga"; + + public const string Bit32RleTopLeft = "Tga/rgb_a_rle_UL.tga"; public const string Bit32RleTopRight = "Tga/rgb_a_rle_UR.tga"; public const string Bit32RleBottomRight = "Tga/rgb_a_rle_LR.tga"; - - public const string Bit16Rle = "Tga/targa_16bit_rle.tga"; - public const string Bit24Rle = "Tga/targa_24bit_rle.tga"; - public const string Bit32Rle = "Tga/targa_32bit_rle.tga"; - public const string Bit16Pal = "Tga/targa_16bit_pal.tga"; - public const string Bit24Pal = "Tga/targa_24bit_pal.tga"; + public const string Bit32RleBottomLeft = "Tga/targa_32bit_rle.tga"; public const string NoAlphaBits16Bit = "Tga/16bit_noalphabits.tga"; public const string NoAlphaBits16BitRle = "Tga/16bit_rle_noalphabits.tga"; diff --git a/tests/Images/Input/Tga/grayscale_LR.tga b/tests/Images/Input/Tga/grayscale_LR.tga new file mode 100644 index 0000000000..01c71b81c5 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed269c8f3bb462d963188d7352ebe85ab20357ac7803e5ac4d7110a23b9e6ddb +size 65580 diff --git a/tests/Images/Input/Tga/grayscale_UL.tga b/tests/Images/Input/Tga/grayscale_UL.tga new file mode 100644 index 0000000000..7670e83f1d --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72c6e1e09b923455e0c8cd14c37b358eb578bc14a0a8fcedde3ab81769960eb7 +size 65580 diff --git a/tests/Images/Input/Tga/grayscale_rle_LR.tga b/tests/Images/Input/Tga/grayscale_rle_LR.tga new file mode 100644 index 0000000000..766d3884c9 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_rle_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a897be6870be2cd183e7678e954767fd12a763c7bfce0f2246f1b7cc1ad08804 +size 31165 diff --git a/tests/Images/Input/Tga/grayscale_rle_UL.tga b/tests/Images/Input/Tga/grayscale_rle_UL.tga new file mode 100644 index 0000000000..699e7ae5b8 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_rle_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f11be4af2283059e869543949588fe19db0e36dec64157ad9a61711cb5e6428 +size 31198 diff --git a/tests/Images/Input/Tga/grayscale_rle_UR.tga b/tests/Images/Input/Tga/grayscale_rle_UR.tga new file mode 100644 index 0000000000..c61503db81 --- /dev/null +++ b/tests/Images/Input/Tga/grayscale_rle_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5aa67ec6d3408fd469ec8e7c5613daf130be893e0b76dee2994a2c32ddae471 +size 31054 diff --git a/tests/Images/Input/Tga/indexed_LR.tga b/tests/Images/Input/Tga/indexed_LR.tga new file mode 100644 index 0000000000..659c3bcea8 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_LR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6d5219fadf7d8b743d35c7e16f11e1182f76351757ff962e0a27f81c357b1fb +size 66315 diff --git a/tests/Images/Input/Tga/indexed_UL.tga b/tests/Images/Input/Tga/indexed_UL.tga new file mode 100644 index 0000000000..da2a3f8ef9 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f42dd07528f9e4f7914a570c027cc845edfe6d3fcdfa45ec8f21bc254cc1f1f +size 66315 diff --git a/tests/Images/Input/Tga/indexed_UR.tga b/tests/Images/Input/Tga/indexed_UR.tga new file mode 100644 index 0000000000..a497383ab8 --- /dev/null +++ b/tests/Images/Input/Tga/indexed_UR.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90d8caa10d3a05f845f94b176a77a2ed85e25b3d460527c96abfe793870c89b8 +size 66315 diff --git a/tests/Images/Input/Tga/rgb_a_UL.tga b/tests/Images/Input/Tga/rgb_a_UL.tga new file mode 100644 index 0000000000..7ee3a52128 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a167af1f8d64119e206593f8944c0b7901393a1b97d703c0121b8a59cae03f4 +size 262188 diff --git a/tests/Images/Input/Tga/rgb_a_rle_UL.tga b/tests/Images/Input/Tga/rgb_a_rle_UL.tga new file mode 100644 index 0000000000..0ea58fd1d6 --- /dev/null +++ b/tests/Images/Input/Tga/rgb_a_rle_UL.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be1323021deead462ef38c17eea5d59aea7467ae33b91bd65b542085e74aa4e4 +size 98427