diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs
index 0d44db8d8..e385fba5e 100644
--- a/src/ImageSharp/Configuration.cs
+++ b/src/ImageSharp/Configuration.cs
@@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Processing;
using SixLabors.Memory;
@@ -150,6 +151,7 @@ namespace SixLabors.ImageSharp
///
///
/// .
+ /// .
///
/// The default configuration of .
internal static Configuration CreateDefaultInstance()
@@ -158,7 +160,8 @@ namespace SixLabors.ImageSharp
new PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
- new BmpConfigurationModule());
+ new BmpConfigurationModule(),
+ new TgaConfigurationModule());
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs
new file mode 100644
index 000000000..e99e8b0c8
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// The options for decoding tga images. Currently empty, but this may change in the future.
+ ///
+ internal interface ITgaDecoderOptions
+ {
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs
new file mode 100644
index 000000000..c7bf6cc93
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// Registers the image encoders, decoders and mime type detectors for the tga format.
+ ///
+ public sealed class TgaConfigurationModule : IConfigurationModule
+ {
+ ///
+ public void Configure(Configuration configuration)
+ {
+ configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, new TgaDecoder());
+ configuration.ImageFormatsManager.AddImageFormatDetector(new TgaImageFormatDetector());
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaConstants.cs b/src/ImageSharp/Formats/Tga/TgaConstants.cs
new file mode 100644
index 000000000..88c98b06a
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaConstants.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Collections.Generic;
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ internal static class TgaConstants
+ {
+ ///
+ /// The list of mimetypes that equate to a targa file.
+ ///
+ public static readonly IEnumerable MimeTypes = new[] { "image/x-tga", "image/x-targa" };
+
+ ///
+ /// The list of file extensions that equate to a targa file.
+ ///
+ public static readonly IEnumerable FileExtensions = new[] { "tga", "vda", "icb", "vst" };
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
new file mode 100644
index 000000000..b97388773
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.IO;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// Image decoder for Truevision TGA images.
+ ///
+ public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector
+ {
+ ///
+ public Image Decode(Configuration configuration, Stream stream)
+ where TPixel : struct, IPixel
+ {
+ Guard.NotNull(stream, nameof(stream));
+
+ return new TgaDecoderCore(configuration, this).Decode(stream);
+ }
+
+ ///
+ public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream);
+
+ ///
+ public IImageInfo Identify(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, nameof(stream));
+
+ return new TgaDecoderCore(configuration, this).Identify(stream);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
new file mode 100644
index 000000000..b8068c082
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -0,0 +1,125 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.IO;
+
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ internal sealed class TgaDecoderCore
+ {
+ ///
+ /// The metadata.
+ ///
+ private ImageMetadata metadata;
+
+ ///
+ /// The file header containing general information about the image.
+ ///
+ private TgaFileHeader fileHeader;
+
+ ///
+ /// The global configuration.
+ ///
+ private readonly Configuration configuration;
+
+ ///
+ /// Used for allocating memory during processing operations.
+ ///
+ private readonly MemoryAllocator memoryAllocator;
+
+ ///
+ /// The stream to decode from.
+ ///
+ private Stream currentStream;
+
+ ///
+ /// The bitmap decoder options.
+ ///
+ private readonly ITgaDecoderOptions options;
+
+ public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options)
+ {
+ this.configuration = configuration;
+ this.memoryAllocator = configuration.MemoryAllocator;
+ this.options = options;
+ }
+
+ ///
+ /// Decodes the image from the specified stream.
+ ///
+ /// The pixel format.
+ /// The stream, where the image should be decoded from. Cannot be null.
+ ///
+ /// is null.
+ ///
+ /// The decoded image.
+ public Image Decode(Stream stream)
+ where TPixel : struct, IPixel
+ {
+ try
+ {
+ this.ReadFileHeader(stream);
+
+ var image = new Image(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
+ Buffer2D pixels = image.GetRootFramePixelBuffer();
+
+ using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(this.fileHeader.Width, 3, 0))
+ {
+ for (int y = 0; y < this.fileHeader.Height; y++)
+ {
+ this.currentStream.Read(row);
+ Span pixelSpan = pixels.GetRowSpan(y);
+ PixelOperations.Instance.FromBgr24Bytes(
+ this.configuration,
+ row.GetSpan(),
+ pixelSpan,
+ this.fileHeader.Width);
+ }
+ }
+
+ return image;
+ }
+ catch (Exception e)
+ {
+ throw new ImageFormatException("TGA image does not have a valid format.", e);
+ }
+ }
+
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public IImageInfo Identify(Stream stream)
+ {
+ this.ReadFileHeader(stream);
+ return new ImageInfo(
+ new PixelTypeInfo(this.fileHeader.PixelDepth),
+ this.fileHeader.Width,
+ this.fileHeader.Height,
+ this.metadata);
+ }
+
+ ///
+ /// Reads the tga file header from the stream.
+ ///
+ /// The containing image data.
+ private void ReadFileHeader(Stream stream)
+ {
+ this.currentStream = stream;
+
+#if NETCOREAPP2_1
+ Span buffer = stackalloc byte[TgaFileHeader.Size];
+#else
+ var buffer = new byte[TgaFileHeader.Size];
+#endif
+ this.currentStream.Read(buffer, 0, TgaFileHeader.Size);
+ this.fileHeader = TgaFileHeader.Parse(buffer);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaFileHeader.cs b/src/ImageSharp/Formats/Tga/TgaFileHeader.cs
new file mode 100644
index 000000000..51390e1b7
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaFileHeader.cs
@@ -0,0 +1,139 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// This block of bytes tells the application detailed information about the targa image.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ internal readonly struct TgaFileHeader
+ {
+ ///
+ /// Defines the size of the data structure in the targa file.
+ ///
+ public const int Size = 18;
+
+ public TgaFileHeader(
+ byte idLength,
+ byte colorMapType,
+ TgaImageType imageType,
+ short cMapStart,
+ short cMapLength,
+ byte cMapDepth,
+ short xOffset,
+ short yOffset,
+ short width,
+ short height,
+ byte pixelDepth,
+ byte imageDescriptor)
+ {
+ this.IdLength = idLength;
+ this.ColorMapType = colorMapType;
+ this.ImageType = imageType;
+ this.CMapStart = cMapStart;
+ this.CMapLength = cMapLength;
+ this.CMapDepth = cMapDepth;
+ this.XOffset = xOffset;
+ this.YOffset = yOffset;
+ this.Width = width;
+ this.Height = height;
+ this.PixelDepth = pixelDepth;
+ this.ImageDescriptor = imageDescriptor;
+ }
+
+ ///
+ /// Gets the id length.
+ /// This field identifies the number of bytes contained in Field 6, the Image ID Field. The maximum number
+ /// of characters is 255. A value of zero indicates that no Image ID field is included with the image.
+ ///
+ public byte IdLength { get; }
+
+ ///
+ /// Gets the color map type.
+ /// This field indicates the type of color map (if any) included with the image. There are currently 2 defined
+ /// values for this field:
+ /// 0 - indicates that no color-map data is included with this image.
+ /// 1 - indicates that a color-map is included with this image.
+ ///
+ public byte ColorMapType { get; }
+
+ ///
+ /// Gets the image type.
+ /// The TGA File Format can be used to store Pseudo-Color, True-Color and Direct-Color images of various
+ /// pixel depths.
+ ///
+ public TgaImageType ImageType { get; }
+
+ ///
+ /// Gets the start of the color map.
+ /// This field and its sub-fields describe the color map (if any) used for the image. If the Color Map Type field
+ /// is set to zero, indicating that no color map exists, then these 5 bytes should be set to zero.
+ ///
+ public short CMapStart { get; }
+
+ ///
+ /// Gets the total number of color map entries included.
+ ///
+ public short CMapLength { get; }
+
+ ///
+ /// Gets the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used.
+ ///
+ public byte CMapDepth { get; }
+
+ ///
+ /// Gets the XOffset.
+ /// These bytes specify the absolute horizontal coordinate for the lower left
+ /// corner of the image as it is positioned on a display device having an
+ /// origin at the lower left of the screen.
+ ///
+ public short XOffset { get; }
+
+ ///
+ /// Gets the YOffset.
+ /// These bytes specify the absolute vertical coordinate for the lower left
+ /// corner of the image as it is positioned on a display device having an
+ /// origin at the lower left of the screen.
+ ///
+ public short YOffset { get; }
+
+ ///
+ /// Gets the width of the image in pixels.
+ ///
+ public short Width { get; }
+
+ ///
+ /// Gets the height of the image in pixels.
+ ///
+ public short Height { get; }
+
+ ///
+ /// Gets the number of bits per pixel. This number includes
+ /// the Attribute or Alpha channel bits. Common values are 8, 16, 24 and
+ /// 32 but other pixel depths could be used.
+ ///
+ public byte PixelDepth { get; }
+
+ ///
+ /// Gets the ImageDescriptor.
+ /// ImageDescriptor contains two pieces of information.
+ /// Bits 0 through 3 contain the number of attribute bits per pixel.
+ /// Attribute bits are found only in pixels for the 16- and 32-bit flavors of the TGA format and are called alpha channel,
+ /// overlay, or interrupt bits. Bits 4 and 5 contain the image origin location (coordinate 0,0) of the image.
+ /// This position may be any of the four corners of the display screen.
+ /// When both of these bits are set to zero, the image origin is the lower-left corner of the screen.
+ /// Bits 6 and 7 of the ImageDescriptor field are unused and should be set to 0.
+ ///
+ public byte ImageDescriptor { get; }
+
+ public static TgaFileHeader Parse(Span data)
+ {
+ return MemoryMarshal.Cast(data)[0];
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaFormat.cs b/src/ImageSharp/Formats/Tga/TgaFormat.cs
new file mode 100644
index 000000000..badb1d77a
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaFormat.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Collections.Generic;
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// Registers the image encoders, decoders and mime type detectors for the tga format.
+ ///
+ public sealed class TgaFormat : IImageFormat
+ {
+ ///
+ /// Gets the current instance.
+ ///
+ public static TgaFormat Instance { get; } = new TgaFormat();
+
+ ///
+ public string Name => "TGA";
+
+ ///
+ public string DefaultMimeType => "image/tga";
+
+ ///
+ public IEnumerable MimeTypes => TgaConstants.MimeTypes;
+
+ ///
+ public IEnumerable FileExtensions => TgaConstants.FileExtensions;
+
+ ///
+ public TgaMetadata CreateDefaultFormatMetadata() => new TgaMetadata();
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaImageFormatDetector.cs b/src/ImageSharp/Formats/Tga/TgaImageFormatDetector.cs
new file mode 100644
index 000000000..e30572847
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaImageFormatDetector.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// Detects tga file headers.
+ ///
+ public sealed class TgaImageFormatDetector : IImageFormatDetector
+ {
+ ///
+ public int HeaderSize => 18;
+
+ ///
+ public IImageFormat DetectFormat(ReadOnlySpan header)
+ {
+ return this.IsSupportedFileFormat(header) ? TgaFormat.Instance : null;
+ }
+
+ private bool IsSupportedFileFormat(ReadOnlySpan header)
+ {
+ return header.Length >= this.HeaderSize;
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaImageType.cs b/src/ImageSharp/Formats/Tga/TgaImageType.cs
new file mode 100644
index 000000000..d8140d5c6
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaImageType.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// Defines the tga image type. The TGA File Format can be used to store Pseudo-Color,
+ /// True-Color and Direct-Color images of various pixel depths.
+ ///
+ public enum TgaImageType : byte
+ {
+ ///
+ /// No image data included.
+ /// Not sure what this is used for.
+ ///
+ NoImageData = 0,
+
+ ///
+ /// Uncompressed, color mapped image.
+ ///
+ ColorMapped = 1,
+
+ ///
+ /// Uncompressed true color image.
+ ///
+ TrueColor = 2,
+
+ ///
+ /// Uncompressed Black and white (grayscale) image.
+ ///
+ BlackAndWhite = 3,
+
+ ///
+ /// Run length encoded, color mapped image.
+ ///
+ RleColorMapped = 9,
+
+ ///
+ /// Run length encoded, true color image.
+ ///
+ RleTrueColor = 10,
+
+ ///
+ /// Run length encoded, black and white (grayscale) image.
+ ///
+ RleBlackAndWhite = 11,
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaMetadata.cs b/src/ImageSharp/Formats/Tga/TgaMetadata.cs
new file mode 100644
index 000000000..185eaedc9
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/TgaMetadata.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Tga
+{
+ ///
+ /// Provides TGA specific metadata information for the image.
+ ///
+ public class TgaMetadata : IDeepCloneable
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TgaMetadata()
+ {
+ }
+
+ ///
+ public IDeepCloneable DeepClone() => throw new System.NotImplementedException();
+ }
+}