Browse Source

Implement TgaMetadata

pull/2751/head
James Jackson-South 2 years ago
parent
commit
1f4605ed3b
  1. 42
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  2. 66
      src/ImageSharp/Formats/Tga/TgaMetadata.cs
  3. 1
      tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs

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

@ -3,8 +3,6 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@ -160,19 +158,26 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> pixels = image.PixelBuffer;
using IMemoryOwner<Rgba32> rgbaOwner = this.memoryAllocator.Allocate<Rgba32>(image.Width);
Span<Rgba32> rgbaRow = rgbaOwner.GetSpan();
for (int y = 0; y < image.Height; y++)
{
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgba32(image.Configuration, pixelRow, rgbaRow);
for (int x = 0; x < image.Width;)
{
TPixel currentPixel = pixelRow[x];
Rgba32 rgba = rgbaRow[x];
byte equalPixelCount = FindEqualPixels(pixelRow, x);
if (equalPixelCount > 0)
{
// Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run.
// Write the number of equal pixels, with the high bit set, indicating it's a compressed pixel run.
stream.WriteByte((byte)(equalPixelCount | 128));
this.WritePixel(stream, currentPixel, currentPixel.ToRgba32());
this.WritePixel(stream, rgba);
x += equalPixelCount + 1;
}
else
@ -180,12 +185,13 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
// Write Raw Packet (i.e., Non-Run-Length Encoded):
byte unEqualPixelCount = FindUnEqualPixels(pixelRow, x);
stream.WriteByte(unEqualPixelCount);
this.WritePixel(stream, currentPixel, currentPixel.ToRgba32());
this.WritePixel(stream, rgba);
x++;
for (int i = 0; i < unEqualPixelCount; i++)
{
currentPixel = pixelRow[x];
this.WritePixel(stream, currentPixel, currentPixel.ToRgba32());
rgba = rgbaRow[x];
this.WritePixel(stream, rgba);
x++;
}
}
@ -196,22 +202,19 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// <summary>
/// Writes a the pixel to the stream.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="stream">The stream to write to.</param>
/// <param name="currentPixel">The current pixel.</param>
/// <param name="color">The color of the pixel to write.</param>
private void WritePixel<TPixel>(Stream stream, TPixel currentPixel, Rgba32 color)
where TPixel : unmanaged, IPixel<TPixel>
private void WritePixel(Stream stream, Rgba32 color)
{
switch (this.bitsPerPixel)
{
case TgaBitsPerPixel.Pixel8:
int luminance = GetLuminance(currentPixel);
stream.WriteByte((byte)luminance);
L8 l8 = L8.FromRgba32(color);
stream.WriteByte(l8.PackedValue);
break;
case TgaBitsPerPixel.Pixel16:
Bgra5551 bgra5551 = new(color.ToVector4());
Bgra5551 bgra5551 = Bgra5551.FromRgba32(color);
Span<byte> buffer = stackalloc byte[2];
BinaryPrimitives.WriteInt16LittleEndian(buffer, (short)bgra5551.PackedValue);
stream.WriteByte(buffer[0]);
@ -402,17 +405,4 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
stream.Write(rowSpan);
}
}
/// <summary>
/// Convert the pixel values to grayscale using ITU-R Recommendation BT.709.
/// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
/// <param name="sourcePixel">The pixel to get the luminance from.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetLuminance<TPixel>(TPixel sourcePixel)
where TPixel : unmanaged, IPixel<TPixel>
{
Vector4 vector = sourcePixel.ToVector4();
return ColorNumerics.GetBT709Luminance(ref vector, 256);
}
}

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

@ -1,12 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tga;
/// <summary>
/// Provides TGA specific metadata information for the image.
/// </summary>
public class TgaMetadata : IDeepCloneable
public class TgaMetadata : IFormatMetadata<TgaMetadata>
{
/// <summary>
/// Initializes a new instance of the <see cref="TgaMetadata"/> class.
@ -33,5 +35,65 @@ public class TgaMetadata : IDeepCloneable
public byte AlphaChannelBits { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new TgaMetadata(this);
public static TgaMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata)
{
// TODO: AlphaChannelBits is not used during encoding.
int bpp = metadata.PixelTypeInfo.BitsPerPixel;
return bpp switch
{
<= 8 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel8 },
<= 16 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel16 },
<= 24 => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel24 },
_ => new TgaMetadata { BitsPerPixel = TgaBitsPerPixel.Pixel32 }
};
}
/// <inheritdoc/>
public FormatConnectingMetadata ToFormatConnectingMetadata()
{
int bpp = (int)this.BitsPerPixel;
PixelComponentInfo info;
PixelColorType color;
PixelAlphaRepresentation alpha;
switch (this.BitsPerPixel)
{
case TgaBitsPerPixel.Pixel8:
info = PixelComponentInfo.Create(1, bpp, 8);
color = PixelColorType.Luminance;
alpha = PixelAlphaRepresentation.None;
break;
case TgaBitsPerPixel.Pixel16:
info = PixelComponentInfo.Create(1, bpp, 5, 5, 5, 1);
color = PixelColorType.BGR | PixelColorType.Alpha;
alpha = PixelAlphaRepresentation.Unassociated;
break;
case TgaBitsPerPixel.Pixel24:
info = PixelComponentInfo.Create(3, bpp, 8, 8, 8);
color = PixelColorType.RGB;
alpha = PixelAlphaRepresentation.None;
break;
case TgaBitsPerPixel.Pixel32 or _:
info = PixelComponentInfo.Create(4, bpp, 8, 8, 8, 8);
color = PixelColorType.RGB | PixelColorType.Alpha;
alpha = PixelAlphaRepresentation.Unassociated;
break;
}
return new()
{
PixelTypeInfo = new PixelTypeInfo(bpp)
{
AlphaRepresentation = alpha,
ComponentInfo = info,
ColorType = color
}
};
}
/// <inheritdoc/>
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
/// <inheritdoc/>
public TgaMetadata DeepClone() => new(this);
}

1
tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs

@ -175,6 +175,7 @@ public class TgaEncoderTests
using (var memStream = new MemoryStream())
{
image.DebugSave(provider, encoder);
image.Save(memStream, encoder);
memStream.Position = 0;
using (var encodedImage = (Image<TPixel>)Image.Load(memStream))

Loading…
Cancel
Save