diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs index 9007b3f5a..df37327c3 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs @@ -24,9 +24,9 @@ internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); + TPixel color = default; color.FromScaledVector4(Vector4.Zero); - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; int offset = 0; for (int y = top; y < top + height; y++) @@ -37,8 +37,8 @@ internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder : TiffBaseColorDecoder : TiffBaseColorDecoder var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; for (int y = top; y < top + height; y++) { @@ -38,18 +38,18 @@ internal class RgbFloat323232TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float r = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float r = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float g = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float g = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float b = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float b = BitConverter.ToSingle(buffer); offset += 4; var colorVector = new Vector4(r, g, b, 1.0f); @@ -61,16 +61,13 @@ internal class RgbFloat323232TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - data.Slice(offset, 4).CopyTo(buffer); - float r = BitConverter.ToSingle(buffer, 0); + float r = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float g = BitConverter.ToSingle(buffer, 0); + float g = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float b = BitConverter.ToSingle(buffer, 0); + float b = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; var colorVector = new Vector4(r, g, b, 1.0f); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs index 920f9fdc4..743502d56 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs @@ -27,7 +27,7 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; for (int y = top; y < top + height; y++) { @@ -38,23 +38,23 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float r = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float r = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float g = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float g = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float b = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float b = BitConverter.ToSingle(buffer); offset += 4; data.Slice(offset, 4).CopyTo(buffer); - Array.Reverse(buffer); - float a = BitConverter.ToSingle(buffer, 0); + buffer.Reverse(); + float a = BitConverter.ToSingle(buffer); offset += 4; var colorVector = new Vector4(r, g, b, a); @@ -66,20 +66,16 @@ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder { for (int x = 0; x < pixelRow.Length; x++) { - data.Slice(offset, 4).CopyTo(buffer); - float r = BitConverter.ToSingle(buffer, 0); + float r = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float g = BitConverter.ToSingle(buffer, 0); + float g = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float b = BitConverter.ToSingle(buffer, 0); + float b = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; - data.Slice(offset, 4).CopyTo(buffer); - float a = BitConverter.ToSingle(buffer, 0); + float a = BitConverter.ToSingle(data.Slice(offset, 4)); offset += 4; var colorVector = new Vector4(r, g, b, a); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs index 78d557f30..f3207b2f4 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -26,7 +26,7 @@ internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder buffer = stackalloc byte[4]; int offset = 0; for (int y = top; y < top + height; y++) @@ -37,8 +37,8 @@ internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder : TiffBaseColorDecoder private readonly MemoryAllocator memoryAllocator; - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] buffer = new byte[4]; - /// /// The global configuration. /// @@ -157,7 +152,9 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor); using TiffStreamWriter writer = new(stream); - long ifdMarker = WriteHeader(writer); + Span buffer = stackalloc byte[4]; + + long ifdMarker = WriteHeader(writer, buffer); Image metadataImage = image; foreach (ImageFrame frame in image.Frames) @@ -171,7 +168,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals long currentOffset = writer.BaseStream.Position; foreach ((long, uint) marker in this.frameMarkers) { - writer.WriteMarkerFast(marker.Item1, marker.Item2); + writer.WriteMarkerFast(marker.Item1, marker.Item2, buffer); } writer.BaseStream.Seek(currentOffset, SeekOrigin.Begin); @@ -181,14 +178,15 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals /// Writes the TIFF file header. /// /// The to write data to. + /// Scratch buffer with minimum size of 2. /// /// The marker to write the first IFD offset. /// - public static long WriteHeader(TiffStreamWriter writer) + public static long WriteHeader(TiffStreamWriter writer, Span buffer) { - writer.Write(ByteOrderMarker); - writer.Write(TiffConstants.HeaderMagicNumber); - return writer.PlaceMarker(); + writer.Write(ByteOrderMarker, buffer); + writer.Write(TiffConstants.HeaderMagicNumber, buffer); + return writer.PlaceMarker(buffer); } /// @@ -307,20 +305,22 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals entries.Sort((a, b) => (ushort)a.Tag - (ushort)b.Tag); - writer.Write((ushort)entries.Count); + Span buffer = stackalloc byte[4]; + + writer.Write((ushort)entries.Count, buffer); foreach (IExifValue entry in entries) { - writer.Write((ushort)entry.Tag); - writer.Write((ushort)entry.DataType); - writer.Write(ExifWriter.GetNumberOfComponents(entry)); + writer.Write((ushort)entry.Tag, buffer); + writer.Write((ushort)entry.DataType, buffer); + writer.Write(ExifWriter.GetNumberOfComponents(entry), buffer); uint length = ExifWriter.GetLength(entry); if (length <= 4) { - int sz = ExifWriter.WriteValue(entry, this.buffer, 0); + int sz = ExifWriter.WriteValue(entry, buffer, 0); DebugGuard.IsTrue(sz == length, "Incorrect number of bytes written"); - writer.WritePadded(this.buffer.AsSpan(0, sz)); + writer.WritePadded(buffer.Slice(0, sz)); } else { @@ -328,12 +328,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals int sz = ExifWriter.WriteValue(entry, raw, 0); DebugGuard.IsTrue(sz == raw.Length, "Incorrect number of bytes written"); largeDataBlocks.Add(raw); - writer.Write(dataOffset); + writer.Write(dataOffset, buffer); dataOffset += (uint)(raw.Length + (raw.Length % 2)); } } - long nextIfdMarker = writer.PlaceMarker(); + long nextIfdMarker = writer.PlaceMarker(buffer); foreach (byte[] dataBlock in largeDataBlocks) { diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index be32ca9ed..3c2ad6084 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -10,13 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers; /// internal sealed class TiffStreamWriter : IDisposable { - private static readonly byte[] PaddingBytes = new byte[4]; - - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] buffer = new byte[4]; - /// /// Initializes a new instance of the class. /// @@ -41,11 +34,12 @@ internal sealed class TiffStreamWriter : IDisposable /// /// Writes an empty four bytes to the stream, returning the offset to be written later. /// + /// Scratch buffer with minimum size of 4. /// The offset to be written later. - public long PlaceMarker() + public long PlaceMarker(Span buffer) { long offset = this.BaseStream.Position; - this.Write(0u); + this.Write(0u, buffer); return offset; } @@ -71,36 +65,38 @@ internal sealed class TiffStreamWriter : IDisposable /// Writes a two-byte unsigned integer to the current stream. /// /// The two-byte unsigned integer to write. - public void Write(ushort value) + /// Scratch buffer with minimum size of 2. + public void Write(ushort value, Span buffer) { if (IsLittleEndian) { - BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value); + BinaryPrimitives.WriteUInt16LittleEndian(buffer, value); } else { - BinaryPrimitives.WriteUInt16BigEndian(this.buffer, value); + BinaryPrimitives.WriteUInt16BigEndian(buffer, value); } - this.BaseStream.Write(this.buffer.AsSpan(0, 2)); + this.BaseStream.Write(buffer.Slice(0, 2)); } /// /// Writes a four-byte unsigned integer to the current stream. /// /// The four-byte unsigned integer to write. - public void Write(uint value) + /// Scratch buffer with minimum size of 4. + public void Write(uint value, Span buffer) { if (IsLittleEndian) { - BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); + BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); } else { - BinaryPrimitives.WriteUInt32BigEndian(this.buffer, value); + BinaryPrimitives.WriteUInt32BigEndian(buffer, value); } - this.BaseStream.Write(this.buffer.AsSpan(0, 4)); + this.BaseStream.Write(buffer.Slice(0, 4)); } /// @@ -113,7 +109,10 @@ internal sealed class TiffStreamWriter : IDisposable if (value.Length % 4 != 0) { - this.BaseStream.Write(PaddingBytes, 0, 4 - (value.Length % 4)); + // No allocation occurs, refers directly to assembly's data segment. + ReadOnlySpan paddingBytes = new byte[4] { 0x00, 0x00, 0x00, 0x00 }; + paddingBytes = paddingBytes[..(4 - (value.Length % 4))]; + this.BaseStream.Write(paddingBytes); } } @@ -122,18 +121,19 @@ internal sealed class TiffStreamWriter : IDisposable /// /// The offset returned when placing the marker /// The four-byte unsigned integer to write. - public void WriteMarker(long offset, uint value) + /// Scratch buffer. + public void WriteMarker(long offset, uint value, Span buffer) { long back = this.BaseStream.Position; this.BaseStream.Seek(offset, SeekOrigin.Begin); - this.Write(value); + this.Write(value, buffer); this.BaseStream.Seek(back, SeekOrigin.Begin); } - public void WriteMarkerFast(long offset, uint value) + public void WriteMarkerFast(long offset, uint value, Span buffer) { this.BaseStream.Seek(offset, SeekOrigin.Begin); - this.Write(value); + this.Write(value, buffer); } /// diff --git a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs index 73ce216d8..4646de7f8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/BigTiffMetadataTests.cs @@ -211,8 +211,8 @@ public class BigTiffMetadataTests foreach (IExifValue entry in values) { - writer.Write((ushort)entry.Tag); - writer.Write((ushort)entry.DataType); + writer.Write((ushort)entry.Tag, buffer); + writer.Write((ushort)entry.DataType, buffer); WriteLong8(writer, buffer, ExifWriter.GetNumberOfComponents(entry)); uint length = ExifWriter.GetLength(entry); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index 790759785..872414730 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -19,7 +19,7 @@ public class TiffEncoderHeaderTests using (TiffStreamWriter writer = new(stream)) { - long firstIfdMarker = TiffEncoderCore.WriteHeader(writer); + long firstIfdMarker = TiffEncoderCore.WriteHeader(writer, stackalloc byte[4]); } Assert.Equal(new byte[] { 0x49, 0x49, 42, 0, 0x00, 0x00, 0x00, 0x00 }, stream.ToArray()); @@ -32,7 +32,7 @@ public class TiffEncoderHeaderTests TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator); using TiffStreamWriter writer = new(stream); - long firstIfdMarker = TiffEncoderCore.WriteHeader(writer); + long firstIfdMarker = TiffEncoderCore.WriteHeader(writer, stackalloc byte[4]); Assert.Equal(4, firstIfdMarker); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index f6a1257f4..9b26ab270 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -53,7 +53,7 @@ public class TiffWriterTests { using var stream = new MemoryStream(); using var writer = new TiffStreamWriter(stream); - writer.Write(1234); + writer.Write(1234, stackalloc byte[2]); Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); } @@ -63,7 +63,7 @@ public class TiffWriterTests { using var stream = new MemoryStream(); using var writer = new TiffStreamWriter(stream); - writer.Write(12345678U); + writer.Write(12345678U, stackalloc byte[4]); Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); } @@ -89,16 +89,17 @@ public class TiffWriterTests public void WriteMarker_WritesToPlacedPosition() { using var stream = new MemoryStream(); + Span buffer = stackalloc byte[4]; using (var writer = new TiffStreamWriter(stream)) { - writer.Write(0x11111111); - long marker = writer.PlaceMarker(); - writer.Write(0x33333333); + writer.Write(0x11111111, buffer); + long marker = writer.PlaceMarker(buffer); + writer.Write(0x33333333, buffer); - writer.WriteMarker(marker, 0x12345678); + writer.WriteMarker(marker, 0x12345678, buffer); - writer.Write(0x44444444); + writer.Write(0x44444444, buffer); } Assert.Equal(