diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
index 47a0e0bbf0..1193eccee3 100644
--- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
@@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.IO;
-using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp
{
diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs
index 9ef7c01c61..f56cb37a81 100644
--- a/src/ImageSharp/Common/Helpers/DebugGuard.cs
+++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs
@@ -37,7 +37,7 @@ namespace SixLabors
/// has a different size than
///
[Conditional("DEBUG")]
- public static void MustBeSameSized(Span target, Span other, string parameterName)
+ public static void MustBeSameSized(ReadOnlySpan target, ReadOnlySpan other, string parameterName)
where T : struct
{
if (target.Length != other.Length)
@@ -57,7 +57,7 @@ namespace SixLabors
/// has less items than
///
[Conditional("DEBUG")]
- public static void MustBeSizedAtLeast(Span target, Span minSpan, string parameterName)
+ public static void MustBeSizedAtLeast(ReadOnlySpan target, ReadOnlySpan minSpan, string parameterName)
where T : struct
{
if (target.Length < minSpan.Length)
diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
index 0ab1413974..83c6389348 100644
--- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(Span scanline, Span previousScanline, int bytesPerPixel)
{
- DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
@@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
/// The bytes per pixel.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel, out int sum)
+ public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum)
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index e8e0aa7043..6a89a1122a 100644
--- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
@@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(Span scanline, Span previousScanline, int bytesPerPixel)
{
- DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
/// The bytes per pixel.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel, out int sum)
+ public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum)
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
index 116154836e..c28b877e41 100644
--- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
/// The bytes per pixel.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(Span scanline, Span result, int bytesPerPixel, out int sum)
+ public static void Encode(ReadOnlySpan scanline, ReadOnlySpan result, int bytesPerPixel, out int sum)
{
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index e0f35293a4..7e0286991b 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(Span scanline, Span previousScanline)
{
- DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
/// The filtered scanline result.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Encode(Span scanline, Span previousScanline, Span result, out int sum)
+ public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, out int sum)
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs
index fd11ba1b6b..7b5f390f12 100644
--- a/src/ImageSharp/Formats/Png/PngChunk.cs
+++ b/src/ImageSharp/Formats/Png/PngChunk.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
-using SixLabors.ImageSharp.Memory;
+using System.Buffers;
namespace SixLabors.ImageSharp.Formats.Png
{
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
internal readonly struct PngChunk
{
- public PngChunk(int length, PngChunkType type, IManagedByteBuffer data = null)
+ public PngChunk(int length, PngChunkType type, IMemoryOwner data = null)
{
this.Length = length;
this.Type = type;
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Gets the data bytes appropriate to the chunk type, if any.
/// This field can be of zero length or null.
///
- public IManagedByteBuffer Data { get; }
+ public IMemoryOwner Data { get; }
///
/// Gets a value indicating whether the given chunk is critical to decoding
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index c2c336c039..36d7001038 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
@@ -84,12 +85,12 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Previous scanline processed.
///
- private IManagedByteBuffer previousScanline;
+ private IMemoryOwner previousScanline;
///
/// The current scanline that is being processed.
///
- private IManagedByteBuffer scanline;
+ private IMemoryOwner scanline;
///
/// The index of the current scanline being processed.
@@ -149,7 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (chunk.Type)
{
case PngChunkType.Header:
- this.ReadHeaderChunk(pngMetadata, chunk.Data.Array);
+ this.ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Physical:
this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan());
@@ -168,29 +169,29 @@ namespace SixLabors.ImageSharp.Formats.Png
break;
case PngChunkType.Palette:
var pal = new byte[chunk.Length];
- Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
+ chunk.Data.GetSpan().CopyTo(pal);
this.palette = pal;
break;
case PngChunkType.Transparency:
var alpha = new byte[chunk.Length];
- Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
+ chunk.Data.GetSpan().CopyTo(alpha);
this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha, pngMetadata);
break;
case PngChunkType.Text:
- this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadTextChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.CompressedText:
- this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadCompressedTextChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.InternationalText:
- this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadInternationalTextChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Exif:
if (!this.ignoreMetadata)
{
var exifData = new byte[chunk.Length];
- Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
+ chunk.Data.GetSpan().CopyTo(exifData);
metadata.ExifProfile = new ExifProfile(exifData);
}
@@ -239,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (chunk.Type)
{
case PngChunkType.Header:
- this.ReadHeaderChunk(pngMetadata, chunk.Data.Array);
+ this.ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Physical:
this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan());
@@ -251,19 +252,19 @@ namespace SixLabors.ImageSharp.Formats.Png
this.SkipChunkDataAndCrc(chunk);
break;
case PngChunkType.Text:
- this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadTextChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.CompressedText:
- this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadCompressedTextChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.InternationalText:
- this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
+ this.ReadInternationalTextChunk(pngMetadata, chunk.Data.GetSpan());
break;
case PngChunkType.Exif:
if (!this.ignoreMetadata)
{
var exifData = new byte[chunk.Length];
- Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
+ chunk.Data.GetSpan().CopyTo(exifData);
metadata.ExifProfile = new ExifProfile(exifData);
}
@@ -312,7 +313,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The number of bits per value.
/// The new array.
/// The resulting array.
- private bool TryScaleUpTo8BitArray(ReadOnlySpan source, int bytesPerScanline, int bits, out IManagedByteBuffer buffer)
+ private bool TryScaleUpTo8BitArray(ReadOnlySpan source, int bytesPerScanline, int bits, out IMemoryOwner buffer)
{
if (bits >= 8)
{
@@ -320,9 +321,9 @@ namespace SixLabors.ImageSharp.Formats.Png
return false;
}
- buffer = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerScanline * 8 / bits, AllocationOptions.Clean);
+ buffer = this.memoryAllocator.Allocate(bytesPerScanline * 8 / bits, AllocationOptions.Clean);
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
- ref byte resultRef = ref buffer.Array[0];
+ ref byte resultRef = ref buffer.GetReference();
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
@@ -504,7 +505,8 @@ namespace SixLabors.ImageSharp.Formats.Png
{
while (this.currentRow < this.header.Height)
{
- int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead);
+ Span scanlineSpan = this.scanline.GetSpan();
+ int bytesRead = compressedStream.Read(scanlineSpan, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead);
this.currentRowBytesRead += bytesRead;
if (this.currentRowBytesRead < this.bytesPerScanline)
{
@@ -512,7 +514,6 @@ namespace SixLabors.ImageSharp.Formats.Png
}
this.currentRowBytesRead = 0;
- Span scanlineSpan = this.scanline.GetSpan();
switch ((FilterType)scanlineSpan[0])
{
@@ -542,7 +543,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.ProcessDefilteredScanline(scanlineSpan, image, pngMetadata);
- this.SwapBuffers();
+ this.SwapScanlineBuffers();
this.currentRow++;
}
}
@@ -576,7 +577,7 @@ namespace SixLabors.ImageSharp.Formats.Png
while (this.currentRow < this.header.Height)
{
- int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead);
+ int bytesRead = compressedStream.Read(this.scanline.GetSpan(), this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead);
this.currentRowBytesRead += bytesRead;
if (this.currentRowBytesRead < bytesPerInterlaceScanline)
{
@@ -617,7 +618,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Span rowSpan = image.GetPixelRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[pass], Adam7.ColumnIncrement[pass]);
- this.SwapBuffers();
+ this.SwapScanlineBuffers();
this.currentRow += Adam7.RowIncrement[pass];
}
@@ -653,70 +654,80 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
// Convert 1, 2, and 4 bit pixel data into the 8 bit equivalent.
- ReadOnlySpan scanlineSpan = this.TryScaleUpTo8BitArray(trimmed, this.bytesPerScanline - 1, this.header.BitDepth, out IManagedByteBuffer buffer)
- ? buffer.GetSpan()
- : trimmed;
-
- switch (this.pngColorType)
+ IMemoryOwner buffer = null;
+ try
{
- case PngColorType.Grayscale:
- PngScanlineProcessor.ProcessGrayscaleScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- pngMetadata.HasTransparency,
- pngMetadata.TransparentL16.GetValueOrDefault(),
- pngMetadata.TransparentL8.GetValueOrDefault());
+ ReadOnlySpan scanlineSpan = this.TryScaleUpTo8BitArray(
+ trimmed,
+ this.bytesPerScanline - 1,
+ this.header.BitDepth,
+ out buffer)
+ ? buffer.GetSpan()
+ : trimmed;
+
+ switch (this.pngColorType)
+ {
+ case PngColorType.Grayscale:
+ PngScanlineProcessor.ProcessGrayscaleScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ pngMetadata.HasTransparency,
+ pngMetadata.TransparentL16.GetValueOrDefault(),
+ pngMetadata.TransparentL8.GetValueOrDefault());
- break;
+ break;
- case PngColorType.GrayscaleWithAlpha:
- PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- this.bytesPerPixel,
- this.bytesPerSample);
+ case PngColorType.GrayscaleWithAlpha:
+ PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ this.bytesPerPixel,
+ this.bytesPerSample);
- break;
+ break;
- case PngColorType.Palette:
- PngScanlineProcessor.ProcessPaletteScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- this.palette,
- this.paletteAlpha);
+ case PngColorType.Palette:
+ PngScanlineProcessor.ProcessPaletteScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ this.palette,
+ this.paletteAlpha);
- break;
+ break;
- case PngColorType.Rgb:
- PngScanlineProcessor.ProcessRgbScanline(
- this.Configuration,
- this.header,
- scanlineSpan,
- rowSpan,
- this.bytesPerPixel,
- this.bytesPerSample,
- pngMetadata.HasTransparency,
- pngMetadata.TransparentRgb48.GetValueOrDefault(),
- pngMetadata.TransparentRgb24.GetValueOrDefault());
+ case PngColorType.Rgb:
+ PngScanlineProcessor.ProcessRgbScanline(
+ this.Configuration,
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ this.bytesPerPixel,
+ this.bytesPerSample,
+ pngMetadata.HasTransparency,
+ pngMetadata.TransparentRgb48.GetValueOrDefault(),
+ pngMetadata.TransparentRgb24.GetValueOrDefault());
- break;
+ break;
- case PngColorType.RgbWithAlpha:
- PngScanlineProcessor.ProcessRgbaScanline(
- this.Configuration,
- this.header,
- scanlineSpan,
- rowSpan,
- this.bytesPerPixel,
- this.bytesPerSample);
+ case PngColorType.RgbWithAlpha:
+ PngScanlineProcessor.ProcessRgbaScanline(
+ this.Configuration,
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ this.bytesPerPixel,
+ this.bytesPerSample);
- break;
+ break;
+ }
+ }
+ finally
+ {
+ buffer?.Dispose();
}
-
- buffer?.Dispose();
}
///
@@ -735,78 +746,88 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
// Convert 1, 2, and 4 bit pixel data into the 8 bit equivalent.
- ReadOnlySpan scanlineSpan = this.TryScaleUpTo8BitArray(trimmed, this.bytesPerScanline, this.header.BitDepth, out IManagedByteBuffer buffer)
- ? buffer.GetSpan()
- : trimmed;
-
- switch (this.pngColorType)
+ IMemoryOwner buffer = null;
+ try
{
- case PngColorType.Grayscale:
- PngScanlineProcessor.ProcessInterlacedGrayscaleScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- pixelOffset,
- increment,
- pngMetadata.HasTransparency,
- pngMetadata.TransparentL16.GetValueOrDefault(),
- pngMetadata.TransparentL8.GetValueOrDefault());
+ ReadOnlySpan scanlineSpan = this.TryScaleUpTo8BitArray(
+ trimmed,
+ this.bytesPerScanline,
+ this.header.BitDepth,
+ out buffer)
+ ? buffer.GetSpan()
+ : trimmed;
+
+ switch (this.pngColorType)
+ {
+ case PngColorType.Grayscale:
+ PngScanlineProcessor.ProcessInterlacedGrayscaleScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ pixelOffset,
+ increment,
+ pngMetadata.HasTransparency,
+ pngMetadata.TransparentL16.GetValueOrDefault(),
+ pngMetadata.TransparentL8.GetValueOrDefault());
- break;
+ break;
- case PngColorType.GrayscaleWithAlpha:
- PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- pixelOffset,
- increment,
- this.bytesPerPixel,
- this.bytesPerSample);
+ case PngColorType.GrayscaleWithAlpha:
+ PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ pixelOffset,
+ increment,
+ this.bytesPerPixel,
+ this.bytesPerSample);
- break;
+ break;
- case PngColorType.Palette:
- PngScanlineProcessor.ProcessInterlacedPaletteScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- pixelOffset,
- increment,
- this.palette,
- this.paletteAlpha);
+ case PngColorType.Palette:
+ PngScanlineProcessor.ProcessInterlacedPaletteScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ pixelOffset,
+ increment,
+ this.palette,
+ this.paletteAlpha);
- break;
+ break;
- case PngColorType.Rgb:
- PngScanlineProcessor.ProcessInterlacedRgbScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- pixelOffset,
- increment,
- this.bytesPerPixel,
- this.bytesPerSample,
- pngMetadata.HasTransparency,
- pngMetadata.TransparentRgb48.GetValueOrDefault(),
- pngMetadata.TransparentRgb24.GetValueOrDefault());
+ case PngColorType.Rgb:
+ PngScanlineProcessor.ProcessInterlacedRgbScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ pixelOffset,
+ increment,
+ this.bytesPerPixel,
+ this.bytesPerSample,
+ pngMetadata.HasTransparency,
+ pngMetadata.TransparentRgb48.GetValueOrDefault(),
+ pngMetadata.TransparentRgb24.GetValueOrDefault());
- break;
+ break;
- case PngColorType.RgbWithAlpha:
- PngScanlineProcessor.ProcessInterlacedRgbaScanline(
- this.header,
- scanlineSpan,
- rowSpan,
- pixelOffset,
- increment,
- this.bytesPerPixel,
- this.bytesPerSample);
+ case PngColorType.RgbWithAlpha:
+ PngScanlineProcessor.ProcessInterlacedRgbaScanline(
+ this.header,
+ scanlineSpan,
+ rowSpan,
+ pixelOffset,
+ increment,
+ this.bytesPerPixel,
+ this.bytesPerSample);
- break;
+ break;
+ }
+ }
+ finally
+ {
+ buffer?.Dispose();
}
-
- buffer?.Dispose();
}
///
@@ -1189,12 +1210,12 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// The length of the chunk data to read.
[MethodImpl(InliningOptions.ShortMethod)]
- private IManagedByteBuffer ReadChunkData(int length)
+ private IMemoryOwner ReadChunkData(int length)
{
// We rent the buffer here to return it afterwards in Decode()
- IManagedByteBuffer buffer = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean);
+ IMemoryOwner buffer = this.Configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean);
- this.currentStream.Read(buffer.Array, 0, length);
+ this.currentStream.Read(buffer.GetSpan(), 0, length);
return buffer;
}
@@ -1272,9 +1293,9 @@ namespace SixLabors.ImageSharp.Formats.Png
return true;
}
- private void SwapBuffers()
+ private void SwapScanlineBuffers()
{
- IManagedByteBuffer temp = this.previousScanline;
+ IMemoryOwner temp = this.previousScanline;
this.previousScanline = this.scanline;
this.scanline = temp;
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 7a285eb70b..4f6fb73567 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -80,32 +80,12 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// The raw data of previous scanline.
///
- private IManagedByteBuffer previousScanline;
+ private IMemoryOwner previousScanline;
///
/// The raw data of current scanline.
///
- private IManagedByteBuffer currentScanline;
-
- ///
- /// The common buffer for the filters.
- ///
- private IManagedByteBuffer filterBuffer;
-
- ///
- /// The ext buffer for the sub filter, .
- ///
- private IManagedByteBuffer subFilter;
-
- ///
- /// The ext buffer for the average filter, .
- ///
- private IManagedByteBuffer averageFilter;
-
- ///
- /// The ext buffer for the Paeth filter, .
- ///
- private IManagedByteBuffer paethFilter;
+ private IMemoryOwner currentScanline;
///
/// Initializes a new instance of the class.
@@ -173,17 +153,8 @@ namespace SixLabors.ImageSharp.Formats.Png
{
this.previousScanline?.Dispose();
this.currentScanline?.Dispose();
- this.subFilter?.Dispose();
- this.averageFilter?.Dispose();
- this.paethFilter?.Dispose();
- this.filterBuffer?.Dispose();
-
this.previousScanline = null;
this.currentScanline = null;
- this.subFilter = null;
- this.averageFilter = null;
- this.paethFilter = null;
- this.filterBuffer = null;
}
///
@@ -278,21 +249,17 @@ namespace SixLabors.ImageSharp.Formats.Png
else
{
// 1, 2, and 4 bit grayscale
- using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer(
- rowSpan.Length,
- AllocationOptions.Clean))
- {
- int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(this.bitDepth) - 1);
- Span tempSpan = temp.GetSpan();
-
- // We need to first create an array of luminance bytes then scale them down to the correct bit depth.
- PixelOperations.Instance.ToL8Bytes(
- this.configuration,
- rowSpan,
- tempSpan,
- rowSpan.Length);
- PngEncoderHelpers.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
- }
+ using IMemoryOwner temp = this.memoryAllocator.Allocate(rowSpan.Length, AllocationOptions.Clean);
+ int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(this.bitDepth) - 1);
+ Span tempSpan = temp.GetSpan();
+
+ // We need to first create an array of luminance bytes then scale them down to the correct bit depth.
+ PixelOperations.Instance.ToL8Bytes(
+ this.configuration,
+ rowSpan,
+ tempSpan,
+ rowSpan.Length);
+ PngEncoderHelpers.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
}
}
@@ -444,6 +411,8 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(rowSpan);
break;
+ case PngColorType.Rgb:
+ case PngColorType.RgbWithAlpha:
default:
this.CollectTPixelBytes(rowSpan);
break;
@@ -451,124 +420,127 @@ namespace SixLabors.ImageSharp.Formats.Png
}
///
- /// Apply filter for the raw scanline.
+ /// Apply the line filter for the raw scanline to enable better compression.
///
- private IManagedByteBuffer FilterPixelBytes()
+ private void FilterPixelBytes(ref Span filter, ref Span attempt)
{
switch (this.options.FilterMethod)
{
case PngFilterMethod.None:
- NoneFilter.Encode(this.currentScanline.GetSpan(), this.filterBuffer.GetSpan());
- return this.filterBuffer;
-
+ NoneFilter.Encode(this.currentScanline.GetSpan(), filter);
+ break;
case PngFilterMethod.Sub:
- SubFilter.Encode(this.currentScanline.GetSpan(), this.filterBuffer.GetSpan(), this.bytesPerPixel, out int _);
- return this.filterBuffer;
+ SubFilter.Encode(this.currentScanline.GetSpan(), filter, this.bytesPerPixel, out int _);
+ break;
case PngFilterMethod.Up:
- UpFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), this.filterBuffer.GetSpan(), out int _);
- return this.filterBuffer;
+ UpFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, out int _);
+ break;
case PngFilterMethod.Average:
- AverageFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), this.filterBuffer.GetSpan(), this.bytesPerPixel, out int _);
- return this.filterBuffer;
+ AverageFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, this.bytesPerPixel, out int _);
+ break;
case PngFilterMethod.Paeth:
- PaethFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), this.filterBuffer.GetSpan(), this.bytesPerPixel, out int _);
- return this.filterBuffer;
-
+ PaethFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, this.bytesPerPixel, out int _);
+ break;
+ case PngFilterMethod.Adaptive:
default:
- return this.GetOptimalFilteredScanline();
+ this.ApplyOptimalFilteredScanline(ref filter, ref attempt);
+ break;
}
}
///
- /// Encodes the pixel data line by line.
- /// Each scanline is encoded in the most optimal manner to improve compression.
+ /// Collects the pixel data line by line for compressing.
+ /// Each scanline is filtered in the most optimal manner to improve compression.
///
/// The pixel format.
/// The row span.
- /// The quantized pixels. Can be null.
- /// The row.
- /// The
- private IManagedByteBuffer EncodePixelRow(ReadOnlySpan rowSpan, IndexedImageFrame quantized, int row)
+ /// The filtered buffer.
+ /// Used for attempting optimized filtering.
+ /// The quantized pixels. Can be .
+ /// The row number.
+ private void CollectAndFilterPixelRow(
+ ReadOnlySpan rowSpan,
+ ref Span filter,
+ ref Span attempt,
+ IndexedImageFrame quantized,
+ int row)
where TPixel : unmanaged, IPixel
{
this.CollectPixelBytes(rowSpan, quantized, row);
- return this.FilterPixelBytes();
+ this.FilterPixelBytes(ref filter, ref attempt);
}
///
/// Encodes the indexed pixel data (with palette) for Adam7 interlaced mode.
///
- /// The row span.
- private IManagedByteBuffer EncodeAdam7IndexedPixelRow(ReadOnlySpan rowSpan)
+ /// The row span.
+ /// The filtered buffer.
+ /// Used for attempting optimized filtering.
+ private void EncodeAdam7IndexedPixelRow(
+ ReadOnlySpan row,
+ ref Span filter,
+ ref Span attempt)
{
// CollectPixelBytes
if (this.bitDepth < 8)
{
- PngEncoderHelpers.ScaleDownFrom8BitArray(rowSpan, this.currentScanline.GetSpan(), this.bitDepth);
+ PngEncoderHelpers.ScaleDownFrom8BitArray(row, this.currentScanline.GetSpan(), this.bitDepth);
}
else
{
- rowSpan.CopyTo(this.currentScanline.GetSpan());
+ row.CopyTo(this.currentScanline.GetSpan());
}
- return this.FilterPixelBytes();
+ this.FilterPixelBytes(ref filter, ref attempt);
}
///
/// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
/// to be most compressible, using lowest total variation as proxy for compressibility.
///
- /// The
- private IManagedByteBuffer GetOptimalFilteredScanline()
+ private void ApplyOptimalFilteredScanline(ref Span filter, ref Span attempt)
{
// Palette images don't compress well with adaptive filtering.
- if (this.options.ColorType == PngColorType.Palette || this.bitDepth < 8)
+ // Nor do images comprising a single row.
+ if (this.options.ColorType == PngColorType.Palette || this.height == 1 || this.bitDepth < 8)
{
- NoneFilter.Encode(this.currentScanline.GetSpan(), this.filterBuffer.GetSpan());
- return this.filterBuffer;
+ NoneFilter.Encode(this.currentScanline.GetSpan(), filter);
+ return;
}
- this.AllocateExtBuffers();
- Span scanSpan = this.currentScanline.GetSpan();
- Span prevSpan = this.previousScanline.GetSpan();
-
- // This order, while different to the enumerated order is more likely to produce a smaller sum
- // early on which shaves a couple of milliseconds off the processing time.
- UpFilter.Encode(scanSpan, prevSpan, this.filterBuffer.GetSpan(), out int currentSum);
+ Span current = this.currentScanline.GetSpan();
+ Span previous = this.previousScanline.GetSpan();
- // TODO: PERF.. We should be breaking out of the encoding for each line as soon as we hit the sum.
- // That way the above comment would actually be true. It used to be anyway...
- // If we could use SIMD for none branching filters we could really speed it up.
- int lowestSum = currentSum;
- IManagedByteBuffer actualResult = this.filterBuffer;
-
- PaethFilter.Encode(scanSpan, prevSpan, this.paethFilter.GetSpan(), this.bytesPerPixel, out currentSum);
-
- if (currentSum < lowestSum)
+ int min = int.MaxValue;
+ SubFilter.Encode(current, attempt, this.bytesPerPixel, out int sum);
+ if (sum < min)
{
- lowestSum = currentSum;
- actualResult = this.paethFilter;
+ min = sum;
+ SwapSpans(ref filter, ref attempt);
}
- SubFilter.Encode(scanSpan, this.subFilter.GetSpan(), this.bytesPerPixel, out currentSum);
-
- if (currentSum < lowestSum)
+ UpFilter.Encode(current, previous, attempt, out sum);
+ if (sum < min)
{
- lowestSum = currentSum;
- actualResult = this.subFilter;
+ min = sum;
+ SwapSpans(ref filter, ref attempt);
}
- AverageFilter.Encode(scanSpan, prevSpan, this.averageFilter.GetSpan(), this.bytesPerPixel, out currentSum);
-
- if (currentSum < lowestSum)
+ AverageFilter.Encode(current, previous, attempt, this.bytesPerPixel, out sum);
+ if (sum < min)
{
- actualResult = this.averageFilter;
+ min = sum;
+ SwapSpans(ref filter, ref attempt);
}
- return actualResult;
+ PaethFilter.Encode(current, previous, attempt, this.bytesPerPixel, out sum);
+ if (sum < min)
+ {
+ SwapSpans(ref filter, ref attempt);
+ }
}
///
@@ -612,8 +584,8 @@ namespace SixLabors.ImageSharp.Formats.Png
int colorTableLength = paletteLength * Unsafe.SizeOf();
bool hasAlpha = false;
- using IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength);
- using IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(paletteLength);
+ using IMemoryOwner colorTable = this.memoryAllocator.Allocate(colorTableLength);
+ using IMemoryOwner alphaTable = this.memoryAllocator.Allocate(paletteLength);
ref Rgb24 colorTableRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(colorTable.GetSpan()));
ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan());
@@ -640,12 +612,12 @@ namespace SixLabors.ImageSharp.Formats.Png
Unsafe.Add(ref alphaTableRef, i) = alpha;
}
- this.WriteChunk(stream, PngChunkType.Palette, colorTable.Array, 0, colorTableLength);
+ this.WriteChunk(stream, PngChunkType.Palette, colorTable.GetSpan(), 0, colorTableLength);
// Write the transparency data
if (hasAlpha)
{
- this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength);
+ this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.GetSpan(), 0, paletteLength);
}
}
@@ -924,38 +896,13 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Allocates the buffers for each scanline.
///
/// The bytes per scanline.
- /// Length of the result.
- private void AllocateBuffers(int bytesPerScanline, int resultLength)
+ private void AllocateScanlineBuffers(int bytesPerScanline)
{
// Clean up from any potential previous runs.
- this.subFilter?.Dispose();
- this.averageFilter?.Dispose();
- this.paethFilter?.Dispose();
- this.subFilter = null;
- this.averageFilter = null;
- this.paethFilter = null;
-
this.previousScanline?.Dispose();
this.currentScanline?.Dispose();
- this.filterBuffer?.Dispose();
- this.previousScanline = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerScanline, AllocationOptions.Clean);
- this.currentScanline = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerScanline, AllocationOptions.Clean);
- this.filterBuffer = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
- }
-
- ///
- /// Allocates the ext buffers for adaptive filter.
- ///
- private void AllocateExtBuffers()
- {
- if (this.subFilter == null)
- {
- int resultLength = this.filterBuffer.Length();
-
- this.subFilter = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
- this.averageFilter = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
- this.paethFilter = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean);
- }
+ this.previousScanline = this.memoryAllocator.Allocate(bytesPerScanline, AllocationOptions.Clean);
+ this.currentScanline = this.memoryAllocator.Allocate(bytesPerScanline, AllocationOptions.Clean);
}
///
@@ -969,17 +916,19 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : unmanaged, IPixel
{
int bytesPerScanline = this.CalculateScanlineLength(this.width);
- int resultLength = bytesPerScanline + 1;
- this.AllocateBuffers(bytesPerScanline, resultLength);
+ int filterLength = bytesPerScanline + 1;
+ this.AllocateScanlineBuffers(bytesPerScanline);
+ using IMemoryOwner filterBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean);
+ using IMemoryOwner attemptBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean);
+
+ Span filter = filterBuffer.GetSpan();
+ Span attempt = attemptBuffer.GetSpan();
for (int y = 0; y < this.height; y++)
{
- IManagedByteBuffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), quantized, y);
- deflateStream.Write(r.Array, 0, resultLength);
-
- IManagedByteBuffer temp = this.currentScanline;
- this.currentScanline = this.previousScanline;
- this.previousScanline = temp;
+ this.CollectAndFilterPixelRow(pixels.GetPixelRowSpan(y), ref filter, ref attempt, quantized, y);
+ deflateStream.Write(filter);
+ this.SwapScanlineBuffers();
}
}
@@ -1004,36 +953,33 @@ namespace SixLabors.ImageSharp.Formats.Png
? ((blockWidth * this.bitDepth) + 7) / 8
: blockWidth * this.bytesPerPixel;
- int resultLength = bytesPerScanline + 1;
+ int filterLength = bytesPerScanline + 1;
+ this.AllocateScanlineBuffers(bytesPerScanline);
- this.AllocateBuffers(bytesPerScanline, resultLength);
+ using IMemoryOwner blockBuffer = this.memoryAllocator.Allocate(blockWidth);
+ using IMemoryOwner filterBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean);
+ using IMemoryOwner attemptBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean);
- using (IMemoryOwner passData = this.memoryAllocator.Allocate(blockWidth))
+ Span block = blockBuffer.GetSpan();
+ Span filter = filterBuffer.GetSpan();
+ Span attempt = attemptBuffer.GetSpan();
+
+ for (int row = startRow; row < height; row += Adam7.RowIncrement[pass])
{
- Span destSpan = passData.Memory.Span;
- for (int row = startRow;
- row < height;
- row += Adam7.RowIncrement[pass])
+ // Collect pixel data
+ Span srcRow = pixels.GetPixelRowSpan(row);
+ for (int col = startCol, i = 0; col < width; col += Adam7.ColumnIncrement[pass])
{
- // collect data
- Span srcRow = pixels.GetPixelRowSpan(row);
- for (int col = startCol, i = 0;
- col < width;
- col += Adam7.ColumnIncrement[pass])
- {
- destSpan[i++] = srcRow[col];
- }
+ block[i++] = srcRow[col];
+ }
- // encode data
- // note: quantized parameter not used
- // note: row parameter not used
- IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan)destSpan, null, -1);
- deflateStream.Write(r.Array, 0, resultLength);
+ // Encode data
+ // Note: quantized parameter not used
+ // Note: row parameter not used
+ this.CollectAndFilterPixelRow(block, ref filter, ref attempt, null, -1);
+ deflateStream.Write(filter);
- IManagedByteBuffer temp = this.currentScanline;
- this.currentScanline = this.previousScanline;
- this.previousScanline = temp;
- }
+ this.SwapScanlineBuffers();
}
}
}
@@ -1059,34 +1005,36 @@ namespace SixLabors.ImageSharp.Formats.Png
? ((blockWidth * this.bitDepth) + 7) / 8
: blockWidth * this.bytesPerPixel;
- int resultLength = bytesPerScanline + 1;
+ int filterLength = bytesPerScanline + 1;
- this.AllocateBuffers(bytesPerScanline, resultLength);
+ this.AllocateScanlineBuffers(bytesPerScanline);
- using (IMemoryOwner passData = this.memoryAllocator.Allocate(blockWidth))
+ using IMemoryOwner blockBuffer = this.memoryAllocator.Allocate(blockWidth);
+ using IMemoryOwner filterBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean);
+ using IMemoryOwner attemptBuffer = this.memoryAllocator.Allocate(filterLength, AllocationOptions.Clean);
+
+ Span block = blockBuffer.GetSpan();
+ Span filter = filterBuffer.GetSpan();
+ Span attempt = attemptBuffer.GetSpan();
+
+ for (int row = startRow;
+ row < height;
+ row += Adam7.RowIncrement[pass])
{
- Span destSpan = passData.Memory.Span;
- for (int row = startRow;
- row < height;
- row += Adam7.RowIncrement[pass])
+ // Collect data
+ ReadOnlySpan srcRow = quantized.GetPixelRowSpan(row);
+ for (int col = startCol, i = 0;
+ col < width;
+ col += Adam7.ColumnIncrement[pass])
{
- // collect data
- ReadOnlySpan srcRow = quantized.GetPixelRowSpan(row);
- for (int col = startCol, i = 0;
- col < width;
- col += Adam7.ColumnIncrement[pass])
- {
- destSpan[i++] = srcRow[col];
- }
+ block[i++] = srcRow[col];
+ }
- // encode data
- IManagedByteBuffer r = this.EncodeAdam7IndexedPixelRow(destSpan);
- deflateStream.Write(r.Array, 0, resultLength);
+ // Encode data
+ this.EncodeAdam7IndexedPixelRow(block, ref filter, ref attempt);
+ deflateStream.Write(filter);
- IManagedByteBuffer temp = this.currentScanline;
- this.currentScanline = this.previousScanline;
- this.previousScanline = temp;
- }
+ this.SwapScanlineBuffers();
}
}
}
@@ -1103,7 +1051,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The to write to.
/// The type of chunk to write.
/// The containing data.
- private void WriteChunk(Stream stream, PngChunkType type, byte[] data) => this.WriteChunk(stream, type, data, 0, data?.Length ?? 0);
+ private void WriteChunk(Stream stream, PngChunkType type, Span data)
+ => this.WriteChunk(stream, type, data, 0, data.Length);
///
/// Writes a chunk of a specified length to the stream at the given offset.
@@ -1113,7 +1062,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The containing data.
/// The position to offset the data at.
/// The of the data to write.
- private void WriteChunk(Stream stream, PngChunkType type, byte[] data, int offset, int length)
+ private void WriteChunk(Stream stream, PngChunkType type, Span data, int offset, int length)
{
BinaryPrimitives.WriteInt32BigEndian(this.buffer, length);
BinaryPrimitives.WriteUInt32BigEndian(this.buffer.AsSpan(4, 4), (uint)type);
@@ -1126,7 +1075,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
stream.Write(data, offset, length);
- crc = Crc32.Calculate(crc, data.AsSpan(offset, length));
+ crc = Crc32.Calculate(crc, data.Slice(offset, length));
}
BinaryPrimitives.WriteUInt32BigEndian(this.buffer, crc);
@@ -1154,5 +1103,19 @@ namespace SixLabors.ImageSharp.Formats.Png
return scanlineLength / mod;
}
+
+ private void SwapScanlineBuffers()
+ {
+ IMemoryOwner temp = this.previousScanline;
+ this.previousScanline = this.currentScanline;
+ this.currentScanline = temp;
+ }
+
+ private static void SwapSpans(ref Span a, ref Span b)
+ {
+ Span t = b;
+ b = a;
+ a = t;
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs
index a9b53e16e8..be9883a700 100644
--- a/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs
@@ -22,9 +22,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
/// The bytes per pixel.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void EncodePaethFilter(Span scanline, Span previousScanline, Span result, int bytesPerPixel, out int sum)
+ public static void EncodePaethFilter(ReadOnlySpan scanline, Span previousScanline, Span result, int bytesPerPixel, out int sum)
{
- DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
+ DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
@@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
/// The bytes per pixel.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void EncodeSubFilter(Span scanline, Span result, int bytesPerPixel, out int sum)
+ public static void EncodeSubFilter(ReadOnlySpan scanline, Span result, int bytesPerPixel, out int sum)
{
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
@@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
/// The filtered scanline result.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void EncodeUpFilter(Span scanline, Span previousScanline, Span result, out int sum)
+ public static void EncodeUpFilter(ReadOnlySpan scanline, Span previousScanline, Span result, out int sum)
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));
@@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
/// The bytes per pixel.
/// The sum of the total variance of the filtered row
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void EncodeAverageFilter(Span scanline, Span previousScanline, Span result, int bytesPerPixel, out int sum)
+ public static void EncodeAverageFilter(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum)
{
DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline));
DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result));