Browse Source

Add support for encoding 24 bit tga files

pull/1026/head
Brian Popow 6 years ago
parent
commit
e7fe67080f
  1. 16
      src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs
  2. 31
      src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs
  3. 1
      src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs
  4. 26
      src/ImageSharp/Formats/Tga/TgaEncoder.cs
  5. 131
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  6. 8
      src/ImageSharp/Formats/Tga/TgaFileHeader.cs
  7. 16
      src/ImageSharp/Formats/Tga/TgaMetadata.cs

16
src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs

@ -0,0 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Tga
{
/// <summary>
/// Configuration options for use during tga encoding.
/// </summary>
internal interface ITgaEncoderOptions
{
/// <summary>
/// Gets the number of bits per pixel.
/// </summary>
TgaBitsPerPixel? BitsPerPixel { get; }
}
}

31
src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Tga
{
/// <summary>
/// Enumerates the available bits per pixel the tga encoder supports.
/// </summary>
public enum TgaBitsPerPixel : byte
{
/// <summary>
/// 8 bits per pixel. Each pixel consists of 1 byte.
/// </summary>
Pixel8 = 8,
/// <summary>
/// 16 bits per pixel. Each pixel consists of 2 bytes.
/// </summary>
Pixel16 = 16,
/// <summary>
/// 24 bits per pixel. Each pixel consists of 3 bytes.
/// </summary>
Pixel24 = 24,
/// <summary>
/// 32 bits per pixel. Each pixel consists of 4 bytes.
/// </summary>
Pixel32 = 32
}
}

1
src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(TgaFormat.Instance, new TgaEncoder());
configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, new TgaDecoder());
configuration.ImageFormatsManager.AddImageFormatDetector(new TgaImageFormatDetector());
}

26
src/ImageSharp/Formats/Tga/TgaEncoder.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tga
{
public sealed class TgaEncoder : IImageEncoder, ITgaEncoderOptions
{
/// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
public TgaBitsPerPixel? BitsPerPixel { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var encoder = new TgaEncoderCore(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
}
}

131
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -0,0 +1,131 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Tga
{
/// <summary>
/// Image encoder for writing an image to a stream as a truevision targa image.
/// </summary>
internal sealed class TgaEncoderCore
{
/// <summary>
/// Used for allocating memory during processing operations.
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary>
/// The color depth, in number of bits per pixel.
/// </summary>
private TgaBitsPerPixel? bitsPerPixel;
/// <summary>
/// Initializes a new instance of the <see cref="TgaEncoderCore"/> class.
/// </summary>
/// <param name="options">The encoder options.</param>
/// <param name="memoryAllocator">The memory manager.</param>
public TgaEncoderCore(ITgaEncoderOptions options, MemoryAllocator memoryAllocator)
{
this.memoryAllocator = memoryAllocator;
this.bitsPerPixel = options.BitsPerPixel;
}
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
TgaMetadata tgaMetadata = metadata.GetFormatMetadata(TgaFormat.Instance);
this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel;
var fileHeader = new TgaFileHeader(
idLength: 0,
colorMapType: 0,
imageType: TgaImageType.TrueColor,
cMapStart: 0,
cMapLength: 0,
cMapDepth: 0,
xOffset: 0,
yOffset: 0,
width: (short)image.Width,
height: (short)image.Height,
pixelDepth: (byte)this.bitsPerPixel.Value,
imageDescriptor: 0);
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[TgaFileHeader.Size];
#else
var buffer = new byte[TgaFileHeader.Size];
#endif
fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, TgaFileHeader.Size);
this.WriteImage(stream, image.Frames.RootFrame);
stream.Flush();
}
/// <summary>
/// Writes the pixel data to the binary stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="image">
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
/// </param>
private void WriteImage<TPixel>(Stream stream, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
Buffer2D<TPixel> pixels = image.PixelBuffer;
switch (this.bitsPerPixel)
{
case TgaBitsPerPixel.Pixel24:
this.Write24Bit(stream, pixels);
break;
}
}
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, 0);
/// <summary>
/// Writes the 24bit pixels uncompressed to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> to write to.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(Stream stream, Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(
this.configuration,
pixelSpan,
row.GetSpan(),
pixelSpan.Length);
stream.Write(row.Array, 0, row.Length());
}
}
}
}
}

8
src/ImageSharp/Formats/Tga/TgaFileHeader.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Tga
@ -135,5 +136,12 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
return MemoryMarshal.Cast<byte, TgaFileHeader>(data)[0];
}
public void WriteTo(Span<byte> buffer)
{
ref TgaFileHeader dest = ref Unsafe.As<byte, TgaFileHeader>(ref MemoryMarshal.GetReference(buffer));
dest = this;
}
}
}

16
src/ImageSharp/Formats/Tga/TgaMetadata.cs

@ -15,7 +15,21 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TgaMetadata"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private TgaMetadata(TgaMetadata other)
{
this.BitsPerPixel = other.BitsPerPixel;
}
/// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Pixel24;
/// <inheritdoc/>
public IDeepCloneable DeepClone() => throw new System.NotImplementedException();
public IDeepCloneable DeepClone() => new TgaMetadata(this);
}
}

Loading…
Cancel
Save