diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
index b48c35e05f..4a283260c5 100644
--- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
@@ -3,6 +3,8 @@
using System;
using System.IO;
+using System.Numerics;
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@@ -61,6 +63,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel;
TgaImageType imageType = this.useCompression ? TgaImageType.RleTrueColor : TgaImageType.TrueColor;
+ if (this.bitsPerPixel == TgaBitsPerPixel.Pixel8)
+ {
+ imageType = this.useCompression ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite;
+ }
+
var fileHeader = new TgaFileHeader(
idLength: 0,
colorMapType: 0,
@@ -141,10 +148,38 @@ namespace SixLabors.ImageSharp.Formats.Tga
TPixel currentPixel = pixelSpan[encodedPixels];
currentPixel.ToRgba32(ref color);
byte equalPixelCount = this.FindEqualPixels(pixelSpan.Slice(encodedPixels));
+
+ // Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run.
stream.WriteByte((byte)(equalPixelCount | 128));
- stream.WriteByte(color.B);
- stream.WriteByte(color.G);
- stream.WriteByte(color.R);
+ switch (this.bitsPerPixel)
+ {
+ case TgaBitsPerPixel.Pixel8:
+ int luminance = GetLuminance(currentPixel);
+ stream.WriteByte((byte)luminance);
+ break;
+
+ case TgaBitsPerPixel.Pixel16:
+ // TODO: this seems to be wrong
+ var bgra5551 = new Bgra5551(color.ToVector4());
+ stream.WriteByte((byte)(bgra5551.PackedValue & 0xFF));
+ stream.WriteByte((byte)(bgra5551.PackedValue & 0xFF00));
+
+ 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;
+ }
+
encodedPixels += equalPixelCount + 1;
}
}
@@ -270,5 +305,25 @@ namespace SixLabors.ImageSharp.Formats.Tga
}
}
}
+
+ ///
+ /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709.
+ ///
+ /// The pixel to get the luminance from
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static int GetLuminance(TPixel sourcePixel)
+ where TPixel : struct, IPixel
+ {
+ Vector4 vector = sourcePixel.ToVector4();
+ return GetLuminance(ref vector);
+ }
+
+ ///
+ /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709.
+ ///
+ /// The vector to get the luminance from
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static int GetLuminance(ref Vector4 vector)
+ => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (256 - 1));
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs
new file mode 100644
index 0000000000..a92b50516d
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.IO;
+
+using SixLabors.ImageSharp.Formats.Tga;
+using SixLabors.ImageSharp.PixelFormats;
+
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Tga
+{
+ using static TestImages.Tga;
+
+ public class TgaEncoderTests
+ {
+ public static readonly TheoryData TgaBitsPerPixelFiles =
+ new TheoryData
+ {
+ { Grey, TgaBitsPerPixel.Pixel8 },
+ { Bit32, TgaBitsPerPixel.Pixel32 },
+ { Bit24, TgaBitsPerPixel.Pixel24 },
+ { Bit16, TgaBitsPerPixel.Pixel16 },
+ };
+
+ [Theory]
+ [MemberData(nameof(TgaBitsPerPixelFiles))]
+ public void Encode_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel)
+ {
+ var options = new TgaEncoder();
+
+ TestFile testFile = TestFile.Create(imagePath);
+ using (Image input = testFile.CreateRgba32Image())
+ {
+ using (var memStream = new MemoryStream())
+ {
+ input.Save(memStream, options);
+ memStream.Position = 0;
+ using (Image output = Image.Load(memStream))
+ {
+ TgaMetadata meta = output.Metadata.GetFormatMetadata(TgaFormat.Instance);
+ Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel);
+ }
+ }
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(TgaBitsPerPixelFiles))]
+ public void Encode_WithCompression_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel)
+ {
+ var options = new TgaEncoder()
+ {
+ Compress = true
+ };
+
+ TestFile testFile = TestFile.Create(imagePath);
+ using (Image input = testFile.CreateRgba32Image())
+ {
+ using (var memStream = new MemoryStream())
+ {
+ input.Save(memStream, options);
+ memStream.Position = 0;
+ using (Image output = Image.Load(memStream))
+ {
+ TgaMetadata meta = output.Metadata.GetFormatMetadata(TgaFormat.Instance);
+ Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel);
+ }
+ }
+ }
+ }
+ }
+}