diff --git a/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs
index a6e9871e0..ef1fecc93 100644
--- a/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs
@@ -12,5 +12,10 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// Gets the number of bits per pixel.
///
TgaBitsPerPixel? BitsPerPixel { get; }
+
+ ///
+ /// Gets a value indicating whether run length compression should be used.
+ ///
+ bool Compress { get; }
}
}
diff --git a/src/ImageSharp/Formats/Tga/TgaEncoder.cs b/src/ImageSharp/Formats/Tga/TgaEncoder.cs
index f5b9fa752..85b4fadfc 100644
--- a/src/ImageSharp/Formats/Tga/TgaEncoder.cs
+++ b/src/ImageSharp/Formats/Tga/TgaEncoder.cs
@@ -8,6 +8,9 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tga
{
+ ///
+ /// Image encoder for writing an image to a stream as a targa truevision image.
+ ///
public sealed class TgaEncoder : IImageEncoder, ITgaEncoderOptions
{
///
@@ -15,6 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
///
public TgaBitsPerPixel? BitsPerPixel { get; set; }
+ ///
+ /// Gets or sets a value indicating whether run length compression should be used.
+ ///
+ public bool Compress { get; set; }
+
///
public void Encode(Image image, Stream stream)
where TPixel : struct, IPixel
diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
index ddd430b05..b48c35e05 100644
--- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
@@ -32,6 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
///
private TgaBitsPerPixel? bitsPerPixel;
+ ///
+ /// Indicates if run length compression should be used.
+ ///
+ private readonly bool useCompression;
+
///
/// Initializes a new instance of the class.
///
@@ -41,6 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.memoryAllocator = memoryAllocator;
this.bitsPerPixel = options.BitsPerPixel;
+ this.useCompression = options.Compress;
}
public void Encode(Image image, Stream stream)
@@ -54,10 +60,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
TgaMetadata tgaMetadata = metadata.GetFormatMetadata(TgaFormat.Instance);
this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel;
+ TgaImageType imageType = this.useCompression ? TgaImageType.RleTrueColor : TgaImageType.TrueColor;
var fileHeader = new TgaFileHeader(
idLength: 0,
colorMapType: 0,
- imageType: TgaImageType.TrueColor,
+ imageType: imageType,
cMapStart: 0,
cMapLength: 0,
cMapDepth: 0,
@@ -77,7 +84,14 @@ namespace SixLabors.ImageSharp.Formats.Tga
stream.Write(buffer, 0, TgaFileHeader.Size);
- this.WriteImage(stream, image.Frames.RootFrame);
+ if (this.useCompression)
+ {
+ this.WriteRunLengthEndcodedImage(stream, image.Frames.RootFrame);
+ }
+ else
+ {
+ this.WriteImage(stream, image.Frames.RootFrame);
+ }
stream.Flush();
}
@@ -114,6 +128,51 @@ namespace SixLabors.ImageSharp.Formats.Tga
}
}
+ private void WriteRunLengthEndcodedImage(Stream stream, ImageFrame image)
+ where TPixel : struct, IPixel
+ {
+ Rgba32 color = default;
+ Buffer2D pixels = image.PixelBuffer;
+ Span pixelSpan = pixels.Span;
+ int totalPixels = image.Width * image.Height;
+ int encodedPixels = 0;
+ while (encodedPixels < totalPixels)
+ {
+ TPixel currentPixel = pixelSpan[encodedPixels];
+ currentPixel.ToRgba32(ref color);
+ byte equalPixelCount = this.FindEqualPixels(pixelSpan.Slice(encodedPixels));
+ stream.WriteByte((byte)(equalPixelCount | 128));
+ stream.WriteByte(color.B);
+ stream.WriteByte(color.G);
+ stream.WriteByte(color.R);
+ encodedPixels += equalPixelCount + 1;
+ }
+ }
+
+ private byte FindEqualPixels(Span pixelSpan)
+ where TPixel : struct, IPixel
+ {
+ int idx = 0;
+ byte equalPixelCount = 0;
+ while (equalPixelCount < 127 && idx < pixelSpan.Length - 1)
+ {
+ TPixel currentPixel = pixelSpan[idx];
+ TPixel nextPixel = pixelSpan[idx + 1];
+ if (currentPixel.Equals(nextPixel))
+ {
+ equalPixelCount++;
+ }
+ else
+ {
+ return equalPixelCount;
+ }
+
+ idx++;
+ }
+
+ return equalPixelCount;
+ }
+
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, 0);
///