Browse Source

Add decoding of 24bit RLE tga images

pull/1026/head
Brian Popow 7 years ago
parent
commit
f6cce786ea
  1. 84
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  2. 24
      src/ImageSharp/Formats/Tga/TgaImageType.cs
  3. 10
      src/ImageSharp/Formats/Tga/TgaThrowHelper.cs

84
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -82,7 +82,15 @@ namespace SixLabors.ImageSharp.Formats.Tga
break;
case 24:
this.ReadBgr24(pixels);
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
this.ReadRle24(pixels, this.fileHeader.Width, this.fileHeader.Height);
}
else
{
this.ReadBgr24(pixels);
}
break;
case 32:
@ -181,6 +189,80 @@ namespace SixLabors.ImageSharp.Formats.Tga
}
}
private void ReadRle24<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
TPixel color = default;
using (IMemoryOwner<byte> buffer = this.memoryAllocator.Allocate<byte>(width * height * 3, AllocationOptions.Clean))
{
Span<byte> bufferSpan = buffer.GetSpan();
this.UncompressRle24(width, bufferSpan);
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelRow = pixels.GetRowSpan(this.fileHeader.Height - y - 1);
for (int x = 0; x < width; x++)
{
int idx = (y * width * 3) + (x * 3);
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref bufferSpan[idx]));
pixelRow[x] = color;
}
}
}
}
private void UncompressRle24(int w, Span<byte> buffer)
{
int uncompressedPixels = 0;
#if NETCOREAPP2_1
Span<byte> pixel = stackalloc byte[3];
#else
var pixel = new byte[3];
#endif
int totalPixels = this.fileHeader.Height * this.fileHeader.Width;
while (uncompressedPixels < totalPixels)
{
byte runLengthByte = (byte)this.currentStream.ReadByte();
// The high bit of the run length value is always 1, to indicate that this is a run-length encoded packet.
int highBit = runLengthByte >> 7;
if (highBit == 1)
{
int runLength = runLengthByte & 127;
if (runLength == 0)
{
// TgaThrowHelper.ThrowImageFormatException("invalid run length of zero");
}
this.currentStream.Read(pixel, 0, 3);
int bufferIdx = uncompressedPixels * 3;
for (int i = 0; i < runLength + 1; i++, uncompressedPixels++)
{
buffer[bufferIdx++] = pixel[0];
buffer[bufferIdx++] = pixel[1];
buffer[bufferIdx++] = pixel[2];
}
}
else
{
// Non-run-length encoded packet.
int runLength = runLengthByte;
if (runLength == 0)
{
// TgaThrowHelper.ThrowImageFormatException("invalid run length of zero");
}
int bufferIdx = uncompressedPixels * 3;
for (int i = 0; i < runLength + 1; i++, uncompressedPixels++)
{
this.currentStream.Read(pixel, 0, 3);
buffer[bufferIdx++] = pixel[0];
buffer[bufferIdx++] = pixel[1];
buffer[bufferIdx++] = pixel[2];
}
}
}
}
private void ReadBgra32<TPixel>(Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{

24
src/ImageSharp/Formats/Tga/TgaImageType.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Tga
namespace SixLabors.
ImageSharp.Formats.Tga
{
/// <summary>
/// Defines the tga image type. The TGA File Format can be used to store Pseudo-Color,
@ -45,4 +46,25 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// </summary>
RleBlackAndWhite = 11,
}
/// <summary>
/// Extension methods for TgaImageType enum.
/// </summary>
public static class TgaImageTypeExtensions
{
/// <summary>
/// Checks if this tga image type is run length encoded.
/// </summary>
/// <param name="imageType">The tga image type.</param>
/// <returns>True, if this image type is run length encoded, otherwise false.</returns>
public static bool IsRunLengthEncoded(this TgaImageType imageType)
{
if (imageType is TgaImageType.RleColorMapped || imageType is TgaImageType.RleBlackAndWhite || imageType is TgaImageType.RleTrueColor)
{
return true;
}
return false;
}
}
}

10
src/ImageSharp/Formats/Tga/TgaThrowHelper.cs

@ -8,6 +8,16 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
internal static class TgaThrowHelper
{
/// <summary>
/// Cold path optimization for throwing <see cref="ImageFormatException"/>-s
/// </summary>
/// <param name="errorMessage">The error message for the exception.</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowImageFormatException(string errorMessage)
{
throw new ImageFormatException(errorMessage);
}
/// <summary>
/// Cold path optimization for throwing <see cref="NotSupportedException"/>-s
/// </summary>

Loading…
Cancel
Save