diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
index 9c32471fd..242876343 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -1,8 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -29,12 +29,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
///
/// The metadata.
///
- private ImageMetadata metadata;
+ private ImageMetadata? metadata;
///
/// The tga specific metadata.
///
- private TgaMetadata tgaMetadata;
+ private TgaMetadata? tgaMetadata;
///
/// The file header containing general information about the image.
@@ -46,11 +46,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
///
private readonly MemoryAllocator memoryAllocator;
- ///
- /// The stream to decode from.
- ///
- private BufferedReadStream currentStream;
-
///
/// Indicates whether there is a alpha channel present.
///
@@ -80,7 +75,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
try
{
TgaImageOrigin origin = this.ReadFileHeader(stream);
- this.currentStream.Skip(this.fileHeader.IdLength);
+ stream.Skip(this.fileHeader.IdLength);
// Parse the color map, if present.
if (this.fileHeader.ColorMapType is not 0 and not 1)
@@ -93,7 +88,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
throw new UnknownImageFormatException("Width or height cannot be 0");
}
- var image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
+ Image image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
if (this.fileHeader.ColorMapType == 1)
@@ -113,7 +108,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
using (IMemoryOwner palette = this.memoryAllocator.Allocate(colorMapSizeInBytes, AllocationOptions.Clean))
{
Span paletteSpan = palette.GetSpan();
- int bytesRead = this.currentStream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes);
+ int bytesRead = stream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes);
if (bytesRead != colorMapSizeInBytes)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read the color map");
@@ -122,6 +117,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (this.fileHeader.ImageType == TgaImageType.RleColorMapped)
{
this.ReadPalettedRle(
+ stream,
this.fileHeader.Width,
this.fileHeader.Height,
pixels,
@@ -132,6 +128,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
else
{
this.ReadPaletted(
+ stream,
this.fileHeader.Width,
this.fileHeader.Height,
pixels,
@@ -148,7 +145,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (this.fileHeader.CMapLength > 0)
{
int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
- this.currentStream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes);
+ stream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes);
}
switch (this.fileHeader.PixelDepth)
@@ -156,11 +153,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 8:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
- this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin);
+ this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin);
}
else
{
- this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
+ this.ReadMonoChrome(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@@ -169,11 +166,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 16:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
- this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin);
+ this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin);
}
else
{
- this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
+ this.ReadBgra16(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@@ -181,11 +178,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 24:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
- this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin);
+ this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin);
}
else
{
- this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
+ this.ReadBgr24(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@@ -193,11 +190,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
case 32:
if (this.fileHeader.ImageType.IsRunLengthEncoded())
{
- this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin);
+ this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin);
}
else
{
- this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
+ this.ReadBgra32(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin);
}
break;
@@ -219,13 +216,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image with a palette.
///
/// The pixel type.
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// The to assign the palette to.
/// The color palette.
/// Color map size of one entry in bytes.
/// The image origin.
- private void ReadPaletted(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
+ private void ReadPaletted(BufferedReadStream stream, int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
TPixel color = default;
@@ -243,14 +241,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
- this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
+ this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
- this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
+ this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
@@ -261,14 +259,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
- this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
+ ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
- this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
+ ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
@@ -279,14 +277,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
- this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
+ ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
- this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
+ ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow);
}
}
@@ -299,48 +297,47 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a run length encoded TGA image with a palette.
///
/// The pixel type.
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// The to assign the palette to.
/// The color palette.
/// Color map size of one entry in bytes.
/// The image origin.
- private void ReadPalettedRle(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
+ private void ReadPalettedRle(BufferedReadStream stream, int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
- using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean))
- {
- TPixel color = default;
- Span bufferSpan = buffer.GetSpan();
- this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1);
+ using IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean);
+ TPixel color = default;
+ Span bufferSpan = buffer.GetSpan();
+ this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel: 1);
- for (int y = 0; y < height; y++)
+ for (int y = 0; y < height; y++)
+ {
+ int newY = InvertY(y, height, origin);
+ Span pixelRow = pixels.DangerousGetRowSpan(newY);
+ int rowStartIdx = y * width;
+ for (int x = 0; x < width; x++)
{
- int newY = InvertY(y, height, origin);
- Span pixelRow = pixels.DangerousGetRowSpan(newY);
- int rowStartIdx = y * width;
- for (int x = 0; x < width; x++)
+ int idx = rowStartIdx + x;
+ switch (colorMapPixelSizeInBytes)
{
- int idx = rowStartIdx + x;
- switch (colorMapPixelSizeInBytes)
- {
- case 1:
- color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
- break;
- case 2:
- this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color);
- break;
- case 3:
- color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
- break;
- case 4:
- color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
- break;
- }
-
- int newX = InvertX(x, width, origin);
- pixelRow[newX] = color;
+ case 1:
+ color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
+ break;
+ case 2:
+ this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color);
+ break;
+ case 3:
+ color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
+ break;
+ case 4:
+ color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
+ break;
}
+
+ int newX = InvertX(x, width, origin);
+ pixelRow[newX] = color;
}
}
}
@@ -349,11 +346,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed monochrome TGA image.
///
/// The pixel type.
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// The to assign the palette to.
/// the image origin.
- private void ReadMonoChrome(int width, int height, Buffer2D pixels, TgaImageOrigin origin)
+ private void ReadMonoChrome(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
bool invertX = InvertX(origin);
@@ -366,7 +364,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Span pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--)
{
- this.ReadL8Pixel(color, x, pixelSpan);
+ ReadL8Pixel(stream, color, x, pixelSpan);
}
}
@@ -380,14 +378,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int y = height - 1; y >= 0; y--)
{
- this.ReadL8Row(width, pixels, rowSpan, y);
+ this.ReadL8Row(stream, width, pixels, rowSpan, y);
}
}
else
{
for (int y = 0; y < height; y++)
{
- this.ReadL8Row(width, pixels, rowSpan, y);
+ this.ReadL8Row(stream, width, pixels, rowSpan, y);
}
}
}
@@ -396,11 +394,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image where each pixels has 16 bit.
///
/// The pixel type.
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// The to assign the palette to.
/// The image origin.
- private void ReadBgra16(int width, int height, Buffer2D pixels, TgaImageOrigin origin)
+ private void ReadBgra16(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
TPixel color = default;
@@ -417,7 +416,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
- int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 2);
+ int bytesRead = stream.Read(this.scratchBuffer, 0, 2);
if (bytesRead != 2)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@@ -442,7 +441,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
else
{
- int bytesRead = this.currentStream.Read(rowSpan);
+ int bytesRead = stream.Read(rowSpan);
if (bytesRead != rowSpan.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@@ -473,11 +472,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image where each pixels has 24 bit.
///
/// The pixel type.
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// The to assign the palette to.
/// The image origin.
- private void ReadBgr24(int width, int height, Buffer2D pixels, TgaImageOrigin origin)
+ private void ReadBgr24(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
bool invertX = InvertX(origin);
@@ -490,7 +490,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Span pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--)
{
- this.ReadBgr24Pixel(color, x, pixelSpan);
+ this.ReadBgr24Pixel(stream, color, x, pixelSpan);
}
}
@@ -505,14 +505,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int y = height - 1; y >= 0; y--)
{
- this.ReadBgr24Row(width, pixels, rowSpan, y);
+ this.ReadBgr24Row(stream, width, pixels, rowSpan, y);
}
}
else
{
for (int y = 0; y < height; y++)
{
- this.ReadBgr24Row(width, pixels, rowSpan, y);
+ this.ReadBgr24Row(stream, width, pixels, rowSpan, y);
}
}
}
@@ -521,15 +521,19 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a uncompressed TGA image where each pixels has 32 bit.
///
/// The pixel type.
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// The to assign the palette to.
/// The image origin.
- private void ReadBgra32(int width, int height, Buffer2D pixels, TgaImageOrigin origin)
+ private void ReadBgra32(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
TPixel color = default;
bool invertX = InvertX(origin);
+
+ Guard.NotNull(this.tgaMetadata);
+
if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX)
{
using IMemoryOwner row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0);
@@ -539,14 +543,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int y = height - 1; y >= 0; y--)
{
- this.ReadBgra32Row(width, pixels, rowSpan, y);
+ this.ReadBgra32Row(stream, width, pixels, rowSpan, y);
}
}
else
{
for (int y = 0; y < height; y++)
{
- this.ReadBgra32Row(width, pixels, rowSpan, y);
+ this.ReadBgra32Row(stream, width, pixels, rowSpan, y);
}
}
@@ -561,14 +565,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{
for (int x = width - 1; x >= 0; x--)
{
- this.ReadBgra32Pixel(x, color, pixelRow);
+ this.ReadBgra32Pixel(stream, x, color, pixelRow);
}
}
else
{
for (int x = 0; x < width; x++)
{
- this.ReadBgra32Pixel(x, color, pixelRow);
+ this.ReadBgra32Pixel(stream, x, color, pixelRow);
}
}
}
@@ -578,70 +582,72 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
/// Reads a run length encoded TGA image.
///
/// The pixel type.
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// The to assign the palette to.
/// The bytes per pixel.
/// The image origin.
- private void ReadRle(int width, int height, Buffer2D pixels, int bytesPerPixel, TgaImageOrigin origin)
+ private void ReadRle(BufferedReadStream stream, int width, int height, Buffer2D pixels, int bytesPerPixel, TgaImageOrigin origin)
where TPixel : unmanaged, IPixel
{
TPixel color = default;
+
+ Guard.NotNull(this.tgaMetadata);
+
byte alphaBits = this.tgaMetadata.AlphaChannelBits;
- using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean))
+ using IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean);
+ Span bufferSpan = buffer.GetSpan();
+ this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel);
+ for (int y = 0; y < height; y++)
{
- Span bufferSpan = buffer.GetSpan();
- this.UncompressRle(width, height, bufferSpan, bytesPerPixel);
- for (int y = 0; y < height; y++)
+ int newY = InvertY(y, height, origin);
+ Span pixelRow = pixels.DangerousGetRowSpan(newY);
+ int rowStartIdx = y * width * bytesPerPixel;
+ for (int x = 0; x < width; x++)
{
- int newY = InvertY(y, height, origin);
- Span pixelRow = pixels.DangerousGetRowSpan(newY);
- int rowStartIdx = y * width * bytesPerPixel;
- for (int x = 0; x < width; x++)
+ int idx = rowStartIdx + (x * bytesPerPixel);
+ switch (bytesPerPixel)
{
- int idx = rowStartIdx + (x * bytesPerPixel);
- switch (bytesPerPixel)
- {
- case 1:
- color.FromL8(Unsafe.As(ref bufferSpan[idx]));
- break;
- case 2:
- if (!this.hasAlpha)
- {
- // Set alpha value to 1, to treat it as opaque for Bgra5551.
- bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128);
- }
-
- if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite)
- {
- color.FromLa16(Unsafe.As(ref bufferSpan[idx]));
- }
- else
- {
- color.FromBgra5551(Unsafe.As(ref bufferSpan[idx]));
- }
-
- break;
- case 3:
- color.FromBgr24(Unsafe.As(ref bufferSpan[idx]));
- break;
- case 4:
- if (this.hasAlpha)
- {
- color.FromBgra32(Unsafe.As(ref bufferSpan[idx]));
- }
- else
- {
- byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
- color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha));
- }
-
- break;
- }
+ case 1:
+ color.FromL8(Unsafe.As(ref bufferSpan[idx]));
+ break;
+ case 2:
+ if (!this.hasAlpha)
+ {
+ // Set alpha value to 1, to treat it as opaque for Bgra5551.
+ bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128);
+ }
+
+ if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite)
+ {
+ color.FromLa16(Unsafe.As(ref bufferSpan[idx]));
+ }
+ else
+ {
+ color.FromBgra5551(Unsafe.As(ref bufferSpan[idx]));
+ }
- int newX = InvertX(x, width, origin);
- pixelRow[newX] = color;
+ break;
+ case 3:
+ color.FromBgr24(Unsafe.As(ref bufferSpan[idx]));
+ break;
+ case 4:
+ if (this.hasAlpha)
+ {
+ color.FromBgra32(Unsafe.As(ref bufferSpan[idx]));
+ }
+ else
+ {
+ byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
+ color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha));
+ }
+
+ break;
}
+
+ int newX = InvertX(x, width, origin);
+ pixelRow[newX] = color;
}
}
}
@@ -658,10 +664,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadL8Row(int width, Buffer2D pixels, Span row, int y)
+ private void ReadL8Row(BufferedReadStream stream, int width, Buffer2D pixels, Span row, int y)
where TPixel : unmanaged, IPixel
{
- int bytesRead = this.currentStream.Read(row);
+ int bytesRead = stream.Read(row);
if (bytesRead != row.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@@ -672,19 +678,19 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadL8Pixel(TPixel color, int x, Span pixelSpan)
+ private static void ReadL8Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan)
where TPixel : unmanaged, IPixel
{
- byte pixelValue = (byte)this.currentStream.ReadByte();
+ byte pixelValue = (byte)stream.ReadByte();
color.FromL8(Unsafe.As(ref pixelValue));
pixelSpan[x] = color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadBgr24Pixel(TPixel color, int x, Span pixelSpan)
+ private void ReadBgr24Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan)
where TPixel : unmanaged, IPixel
{
- int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 3);
+ int bytesRead = stream.Read(this.scratchBuffer, 0, 3);
if (bytesRead != 3)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel");
@@ -695,10 +701,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadBgr24Row(int width, Buffer2D pixels, Span row, int y)
+ private void ReadBgr24Row(BufferedReadStream stream, int width, Buffer2D pixels, Span row, int y)
where TPixel : unmanaged, IPixel
{
- int bytesRead = this.currentStream.Read(row);
+ int bytesRead = stream.Read(row);
if (bytesRead != row.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@@ -709,25 +715,27 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadBgra32Pixel(int x, TPixel color, Span pixelRow)
+ private void ReadBgra32Pixel(BufferedReadStream stream, int x, TPixel color, Span pixelRow)
where TPixel : unmanaged, IPixel
{
- int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 4);
+ int bytesRead = stream.Read(this.scratchBuffer, 0, 4);
if (bytesRead != 4)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel");
}
+ Guard.NotNull(this.tgaMetadata);
+
byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3];
color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha));
pixelRow[x] = color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadBgra32Row(int width, Buffer2D pixels, Span row, int y)
+ private void ReadBgra32Row(BufferedReadStream stream, int width, Buffer2D pixels, Span row, int y)
where TPixel : unmanaged, IPixel
{
- int bytesRead = this.currentStream.Read(row);
+ int bytesRead = stream.Read(row);
if (bytesRead != row.Length)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@@ -738,10 +746,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadPalettedBgra16Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow)
+ private void ReadPalettedBgra16Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow)
where TPixel : unmanaged, IPixel
{
- int colorIndex = this.currentStream.ReadByte();
+ int colorIndex = stream.ReadByte();
if (colorIndex == -1)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@@ -768,10 +776,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadPalettedBgr24Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow)
+ private static void ReadPalettedBgr24Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow)
where TPixel : unmanaged, IPixel
{
- int colorIndex = this.currentStream.ReadByte();
+ int colorIndex = stream.ReadByte();
if (colorIndex == -1)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@@ -782,10 +790,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void ReadPalettedBgra32Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow)
+ private static void ReadPalettedBgra32Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow)
where TPixel : unmanaged, IPixel
{
- int colorIndex = this.currentStream.ReadByte();
+ int colorIndex = stream.ReadByte();
if (colorIndex == -1)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index");
@@ -798,25 +806,26 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
///
/// Produce uncompressed tga data from a run length encoded stream.
///
+ /// The containing image data.
/// The width of the image.
/// The height of the image.
/// Buffer for uncompressed data.
/// The bytes used per pixel.
- private void UncompressRle(int width, int height, Span buffer, int bytesPerPixel)
+ private void UncompressRle(BufferedReadStream stream, int width, int height, Span buffer, int bytesPerPixel)
{
int uncompressedPixels = 0;
Span pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel);
int totalPixels = width * height;
while (uncompressedPixels < totalPixels)
{
- byte runLengthByte = (byte)this.currentStream.ReadByte();
+ byte runLengthByte = (byte)stream.ReadByte();
// The high bit of a run length packet is set to 1.
int highBit = runLengthByte >> 7;
if (highBit == 1)
{
int runLength = runLengthByte & 127;
- int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel);
+ int bytesRead = stream.Read(pixel, 0, bytesPerPixel);
if (bytesRead != bytesPerPixel)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream");
@@ -836,7 +845,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
int bufferIdx = uncompressedPixels * bytesPerPixel;
for (int i = 0; i < runLength + 1; i++, uncompressedPixels++)
{
- int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel);
+ int bytesRead = stream.Read(pixel, 0, bytesPerPixel);
if (bytesRead != bytesPerPixel)
{
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream");
@@ -917,13 +926,13 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
///
/// The containing image data.
/// The image origin.
+ [MemberNotNull(nameof(metadata))]
+ [MemberNotNull(nameof(tgaMetadata))]
private TgaImageOrigin ReadFileHeader(BufferedReadStream stream)
{
- this.currentStream = stream;
-
Span buffer = stackalloc byte[TgaFileHeader.Size];
- this.currentStream.Read(buffer, 0, TgaFileHeader.Size);
+ stream.Read(buffer, 0, TgaFileHeader.Size);
this.fileHeader = TgaFileHeader.Parse(buffer);
this.metadata = new ImageMetadata();
this.tgaMetadata = this.metadata.GetTgaMetadata();
@@ -939,7 +948,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
this.hasAlpha = alphaBits > 0;
// Bits 4 and 5 describe the image origin.
- var origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4);
- return origin;
+ return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4);
}
}
diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
index 44799c894..f468ab9ae 100644
--- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using System.Buffers;
using System.Buffers.Binary;
@@ -23,11 +22,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
///
private readonly MemoryAllocator memoryAllocator;
- ///
- /// The global configuration.
- ///
- private Configuration configuration;
-
///
/// Reusable buffer for writing data.
///
@@ -68,7 +62,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
TgaMetadata tgaMetadata = metadata.GetTgaMetadata();
this.bitsPerPixel ??= tgaMetadata.BitsPerPixel;
@@ -124,7 +117,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
}
else
{
- this.WriteImage(stream, image.Frames.RootFrame);
+ this.WriteImage(image.GetConfiguration(), stream, image.Frames.RootFrame);
}
stream.Flush();
@@ -134,30 +127,31 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the pixel data to the binary stream.
///
/// The pixel format.
+ /// The global configuration.
/// The to write to.
///
/// The containing pixel data.
///
- private void WriteImage(Stream stream, ImageFrame image)
+ private void WriteImage(Configuration configuration, Stream stream, ImageFrame image)
where TPixel : unmanaged, IPixel
{
Buffer2D pixels = image.PixelBuffer;
switch (this.bitsPerPixel)
{
case TgaBitsPerPixel.Pixel8:
- this.Write8Bit(stream, pixels);
+ this.Write8Bit(configuration, stream, pixels);
break;
case TgaBitsPerPixel.Pixel16:
- this.Write16Bit(stream, pixels);
+ this.Write16Bit(configuration, stream, pixels);
break;
case TgaBitsPerPixel.Pixel24:
- this.Write24Bit(stream, pixels);
+ this.Write24Bit(configuration, stream, pixels);
break;
case TgaBitsPerPixel.Pixel32:
- this.Write32Bit(stream, pixels);
+ this.Write32Bit(configuration, stream, pixels);
break;
}
}
@@ -227,7 +221,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
case TgaBitsPerPixel.Pixel16:
Bgra5551 bgra5551 = new(color.ToVector4());
- BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue);
+ BinaryPrimitives.WriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue);
stream.WriteByte(this.buffer[0]);
stream.WriteByte(this.buffer[1]);
@@ -321,9 +315,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 8bit pixels uncompressed to the stream.
///
/// The pixel format.
+ /// The global configuration.
/// The to write to.
/// The containing pixel data.
- private void Write8Bit(Stream stream, Buffer2D pixels)
+ private void Write8Bit(Configuration configuration, Stream stream, Buffer2D pixels)
where TPixel : unmanaged, IPixel
{
using IMemoryOwner row = this.AllocateRow(pixels.Width, 1);
@@ -333,7 +328,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations.Instance.ToL8Bytes(
- this.configuration,
+ configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);
@@ -345,9 +340,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 16bit pixels uncompressed to the stream.
///
/// The pixel format.
+ /// The global configuration.
/// The to write to.
/// The containing pixel data.
- private void Write16Bit(Stream stream, Buffer2D pixels)
+ private void Write16Bit(Configuration configuration, Stream stream, Buffer2D pixels)
where TPixel : unmanaged, IPixel
{
using IMemoryOwner row = this.AllocateRow(pixels.Width, 2);
@@ -357,7 +353,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations.Instance.ToBgra5551Bytes(
- this.configuration,
+ configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);
@@ -369,9 +365,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 24bit pixels uncompressed to the stream.
///
/// The pixel format.
+ /// The global configuration.
/// The to write to.
/// The containing pixel data.
- private void Write24Bit(Stream stream, Buffer2D pixels)
+ private void Write24Bit(Configuration configuration, Stream stream, Buffer2D pixels)
where TPixel : unmanaged, IPixel
{
using IMemoryOwner row = this.AllocateRow(pixels.Width, 3);
@@ -381,7 +378,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations.Instance.ToBgr24Bytes(
- this.configuration,
+ configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);
@@ -393,9 +390,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// Writes the 32bit pixels uncompressed to the stream.
///
/// The pixel format.
+ /// The global configuration.
/// The to write to.
/// The containing pixel data.
- private void Write32Bit(Stream stream, Buffer2D pixels)
+ private void Write32Bit(Configuration configuration, Stream stream, Buffer2D pixels)
where TPixel : unmanaged, IPixel
{
using IMemoryOwner row = this.AllocateRow(pixels.Width, 4);
@@ -405,7 +403,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
{
Span pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations.Instance.ToBgra32Bytes(
- this.configuration,
+ configuration,
pixelSpan,
rowSpan,
pixelSpan.Length);