diff --git a/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs b/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
index 0414840a85..bb75cbabf8 100644
--- a/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
+++ b/src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
@@ -56,6 +56,16 @@ internal readonly struct FrameControl
///
public int YOffset { get; }
+ ///
+ /// Gets the X limit at which to render the following frame
+ ///
+ public uint XLimit => (uint)(this.XOffset + this.Width);
+
+ ///
+ /// Gets the Y limit at which to render the following frame
+ ///
+ public uint YLimit => (uint)(this.YOffset + this.Height);
+
///
/// Gets the frame delay fraction numerator
///
@@ -104,14 +114,14 @@ internal readonly struct FrameControl
PngThrowHelper.ThrowInvalidParameter(this.Height, "Expected > 0");
}
- if (this.XOffset + this.Width > hdr.Width)
+ if (this.XLimit > hdr.Width)
{
- PngThrowHelper.ThrowInvalidParameter(this.XOffset, this.Width, $"The sum > {nameof(PngHeader)}.{nameof(PngHeader.Width)}");
+ PngThrowHelper.ThrowInvalidParameter(this.XOffset, this.Width, $"The sum of them > {nameof(PngHeader)}.{nameof(PngHeader.Width)}");
}
- if (this.YOffset + this.Height > hdr.Height)
+ if (this.YLimit > hdr.Height)
{
- PngThrowHelper.ThrowInvalidParameter(this.YOffset, this.Height, "The sum > PngHeader.Height");
+ PngThrowHelper.ThrowInvalidParameter(this.YOffset, this.Height, $"The sum of them > {nameof(PngHeader)}.{nameof(PngHeader.Height)}");
}
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 618ca42dfa..a53844b4a7 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -228,6 +228,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.currentStream.Position += 4; // Skip sequence number
return length - 4;
},
+ lastFrameControl.Value,
cancellationToken);
lastFrameControl = null;
break;
@@ -237,7 +238,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.InitializeImage(metadata, lastFrameControl, out image);
}
- this.ReadScanlines(chunk.Length, image.Frames.RootFrame, pngMetadata, this.ReadNextDataChunk, cancellationToken);
+ FrameControl frameControl = lastFrameControl ?? new(0, this.header.Width, this.header.Height, 0, 0, 0, 0, default, default);
+
+ this.ReadScanlines(chunk.Length, image.Frames.RootFrame, pngMetadata, this.ReadNextDataChunk, frameControl, cancellationToken);
lastFrameControl = null;
break;
case PngChunkType.Palette:
@@ -682,8 +685,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// The pixel data.
/// The png metadata
/// A delegate to get more data from the inner stream for .
+ /// The frame control
/// The cancellation token.
- private void ReadScanlines(int chunkLength, ImageFrame image, PngMetadata pngMetadata, Func getData, CancellationToken cancellationToken)
+ private void ReadScanlines(int chunkLength, ImageFrame image, PngMetadata pngMetadata, Func getData, FrameControl frameControl, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
using ZlibInflateStream deframeStream = new(this.currentStream, getData);
@@ -692,11 +696,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
if (this.header.InterlaceMethod is PngInterlaceMode.Adam7)
{
- this.DecodeInterlacedPixelData(dataStream, image, pngMetadata, cancellationToken);
+ this.DecodeInterlacedPixelData(frameControl, dataStream, image, pngMetadata, cancellationToken);
}
else
{
- this.DecodePixelData(dataStream, image, pngMetadata, cancellationToken);
+ this.DecodePixelData(frameControl, dataStream, image, pngMetadata, cancellationToken);
}
}
@@ -704,16 +708,17 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// Decodes the raw pixel data row by row
///
/// The pixel format.
+ /// The frame control
/// The compressed pixel data stream.
/// The image to decode to.
/// The png metadata
/// The CancellationToken
- private void DecodePixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata, CancellationToken cancellationToken)
+ private void DecodePixelData(FrameControl frameControl, DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- int currentRow = Adam7.FirstRow[0];
+ int currentRow = frameControl.YOffset;
int currentRowBytesRead = 0;
- int height = image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata) ? frameMetadata.Height : this.header.Height;
+ int height = frameControl.Height;
while (currentRow < height)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -757,7 +762,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
break;
}
- this.ProcessDefilteredScanline(currentRow, scanlineSpan, image, pngMetadata);
+ this.ProcessDefilteredScanline(frameControl, currentRow, scanlineSpan, image, pngMetadata);
this.SwapScanlineBuffers();
currentRow++;
@@ -769,23 +774,19 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
///
///
/// The pixel format.
+ /// The frame control
/// The compressed pixel data stream.
/// The current image.
/// The png metadata.
/// The cancellation token.
- private void DecodeInterlacedPixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata, CancellationToken cancellationToken)
+ private void DecodeInterlacedPixelData(FrameControl frameControl, DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- int currentRow = Adam7.FirstRow[0];
+ int currentRow = Adam7.FirstRow[0] + frameControl.YOffset;
int currentRowBytesRead = 0;
int pass = 0;
- int width = this.header.Width;
- int height = this.header.Height;
- if (image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata))
- {
- width = frameMetadata.Width;
- height = frameMetadata.Height;
- }
+ int width = frameControl.Width;
+ int height = frameControl.Height;
Buffer2D imageBuffer = image.PixelBuffer;
while (true)
@@ -848,7 +849,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
}
Span rowSpan = imageBuffer.DangerousGetRowSpan(currentRow);
- this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[pass], Adam7.ColumnIncrement[pass]);
+ this.ProcessInterlacedDefilteredScanline(frameControl, this.scanline.GetSpan(), rowSpan, pngMetadata, pixelOffset: Adam7.FirstColumn[pass], increment: Adam7.ColumnIncrement[pass]);
this.SwapScanlineBuffers();
@@ -874,11 +875,12 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// Processes the de-filtered scanline filling the image pixel data
///
/// The pixel format.
+ /// The frame control
/// The index of the current scanline being processed.
/// The de-filtered scanline
/// The image
/// The png metadata.
- private void ProcessDefilteredScanline(int currentRow, ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetadata pngMetadata)
+ private void ProcessDefilteredScanline(FrameControl frameControl, int currentRow, ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetadata pngMetadata)
where TPixel : unmanaged, IPixel
{
Span rowSpan = pixels.PixelBuffer.DangerousGetRowSpan(currentRow);
@@ -902,7 +904,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
{
case PngColorType.Grayscale:
PngScanlineProcessor.ProcessGrayscaleScanline(
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
pngMetadata.HasTransparency,
@@ -913,7 +916,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.GrayscaleWithAlpha:
PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline(
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
(uint)this.bytesPerPixel,
@@ -923,7 +927,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Palette:
PngScanlineProcessor.ProcessPaletteScanline(
- this.header,
+ frameControl,
scanlineSpan,
rowSpan,
this.palette,
@@ -933,8 +937,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline(
- this.configuration,
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
this.bytesPerPixel,
@@ -947,8 +951,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline(
- this.configuration,
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
this.bytesPerPixel,
@@ -967,12 +971,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// Processes the interlaced de-filtered scanline filling the image pixel data
///
/// The pixel format.
+ /// The frame control
/// The de-filtered scanline
/// The current image row.
/// The png metadata.
/// The column start index. Always 0 for none interlaced images.
/// The column increment. Always 1 for none interlaced images.
- private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1)
+ private void ProcessInterlacedDefilteredScanline(FrameControl frameControl, ReadOnlySpan defilteredScanline, Span rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1)
where TPixel : unmanaged, IPixel
{
// Trim the first marker byte from the buffer
@@ -994,7 +999,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
{
case PngColorType.Grayscale:
PngScanlineProcessor.ProcessInterlacedGrayscaleScanline(
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
(uint)pixelOffset,
@@ -1007,7 +1013,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.GrayscaleWithAlpha:
PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline(
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
(uint)pixelOffset,
@@ -1019,7 +1026,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Palette:
PngScanlineProcessor.ProcessInterlacedPaletteScanline(
- this.header,
+ frameControl,
scanlineSpan,
rowSpan,
(uint)pixelOffset,
@@ -1031,7 +1038,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Rgb:
PngScanlineProcessor.ProcessInterlacedRgbScanline(
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
(uint)pixelOffset,
@@ -1046,7 +1054,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessInterlacedRgbaScanline(
- this.header,
+ this.header.BitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
(uint)pixelOffset,
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index cc654b2e75..1e7426226a 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -172,23 +172,24 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
{
this.WriteAnimationControlChunk(stream, targetImage.Frames.Count, pngMetadata.NumberPlays);
- this.WriteFrameControlChunk(stream, targetImage.Frames.RootFrame.Metadata.GetPngFrameMetadata(), 0);
- _ = this.WriteDataChunks(targetImage.Frames.RootFrame, rootQuantized, stream, false);
+ FrameControl frameControl = this.WriteFrameControlChunk(stream, targetImage.Frames.RootFrame.Metadata.GetPngFrameMetadata(), 0);
+ _ = this.WriteDataChunks(frameControl, targetImage.Frames.RootFrame, rootQuantized, stream, false);
int index = 1;
foreach (ImageFrame imageFrame in ((IEnumerable>)targetImage.Frames).Skip(1))
{
- this.WriteFrameControlChunk(stream, imageFrame.Metadata.GetPngFrameMetadata(), index);
+ frameControl = this.WriteFrameControlChunk(stream, imageFrame.Metadata.GetPngFrameMetadata(), index);
index++;
IndexedImageFrame? quantized = this.CreateQuantizedImageAndUpdateBitDepth(imageFrame);
- index += this.WriteDataChunks(imageFrame, quantized, stream, true, index);
+ index += this.WriteDataChunks(frameControl, imageFrame, quantized, stream, true);
quantized?.Dispose();
}
}
else
{
- _ = this.WriteDataChunks(targetImage.Frames.RootFrame, rootQuantized, stream, false);
+ FrameControl frameControl = new(0, this.width, this.height, 0, 0, 0, 0, default, default);
+ _ = this.WriteDataChunks(frameControl, targetImage.Frames.RootFrame, rootQuantized, stream, false);
rootQuantized?.Dispose();
}
@@ -962,25 +963,27 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// The containing image data.
/// Provides APng specific metadata information for the image frame.
/// Sequence number.
- private void WriteFrameControlChunk(Stream stream, PngFrameMetadata frameMetadata, int sequenceNumber)
+ private FrameControl WriteFrameControlChunk(Stream stream, PngFrameMetadata frameMetadata, int sequenceNumber)
{
FrameControl fcTL = FrameControl.FromMetadata(frameMetadata, sequenceNumber);
fcTL.WriteTo(this.chunkDataBuffer.Span);
this.WriteChunk(stream, PngChunkType.FrameControl, this.chunkDataBuffer.Span, 0, FrameControl.Size);
+
+ return fcTL;
}
///
/// Writes the pixel information to the stream.
///
/// The pixel format.
+ /// The frame control
/// The frame.
/// The quantized pixel data. Can be null.
/// The stream.
/// Is writing fdAT or IDAT.
- /// Start sequence number.
- private int WriteDataChunks(ImageFrame pixels, IndexedImageFrame? quantized, Stream stream, bool isFrame, int startSequenceNumber = 0)
+ private int WriteDataChunks(FrameControl frameControl, ImageFrame pixels, IndexedImageFrame? quantized, Stream stream, bool isFrame)
where TPixel : unmanaged, IPixel
{
byte[] buffer;
@@ -994,16 +997,16 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
{
if (quantized is not null)
{
- this.EncodeAdam7IndexedPixels(quantized, deflateStream);
+ this.EncodeAdam7IndexedPixels(frameControl, quantized, deflateStream);
}
else
{
- this.EncodeAdam7Pixels(pixels, deflateStream);
+ this.EncodeAdam7Pixels(frameControl, pixels, deflateStream);
}
}
else
{
- this.EncodePixels(pixels, quantized, deflateStream);
+ this.EncodePixels(frameControl, pixels, quantized, deflateStream);
}
}
@@ -1038,7 +1041,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
if (isFrame)
{
byte[] chunkBuffer = new byte[MaxBlockSize];
- BinaryPrimitives.WriteInt32BigEndian(chunkBuffer, startSequenceNumber + i);
+ BinaryPrimitives.WriteInt32BigEndian(chunkBuffer, frameControl.SequenceNumber + 1 + i);
buffer.AsSpan().Slice(i * maxBlockSize, length).CopyTo(chunkBuffer.AsSpan(4, length));
this.WriteChunk(stream, PngChunkType.FrameData, chunkBuffer, 0, length + 4);
@@ -1069,19 +1072,15 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Encodes the pixels.
///
/// The type of the pixel.
+ /// The frame control
/// The pixels.
/// The quantized pixels span.
/// The deflate stream.
- private void EncodePixels(ImageFrame pixels, IndexedImageFrame? quantized, ZlibDeflateStream deflateStream)
+ private void EncodePixels(FrameControl frameControl, ImageFrame pixels, IndexedImageFrame? quantized, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel
{
- int width = this.width;
- int height = this.height;
- if (pixels.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? pngMetadata))
- {
- width = pngMetadata.Width;
- height = pngMetadata.Height;
- }
+ int width = frameControl.Width;
+ int height = frameControl.Height;
int bytesPerScanline = this.CalculateScanlineLength(width);
int filterLength = bytesPerScanline + 1;
@@ -1094,7 +1093,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
{
Span filter = filterBuffer.GetSpan();
Span attempt = attemptBuffer.GetSpan();
- for (int y = 0; y < height; y++)
+ for (int y = frameControl.YOffset; y < frameControl.YLimit; y++)
{
this.CollectAndFilterPixelRow(accessor.GetRowSpan(y), ref filter, ref attempt, quantized, y);
deflateStream.Write(filter);
@@ -1107,18 +1106,19 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Interlaced encoding the pixels.
///
/// The type of the pixel.
+ /// The frame control
/// The image frame.
/// The deflate stream.
- private void EncodeAdam7Pixels(ImageFrame frame, ZlibDeflateStream deflateStream)
+ private void EncodeAdam7Pixels(FrameControl frameControl, ImageFrame frame, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel
{
- int width = frame.Width;
- int height = frame.Height;
+ int width = frameControl.Width;
+ int height = frameControl.Height;
Buffer2D pixelBuffer = frame.PixelBuffer;
for (int pass = 0; pass < 7; pass++)
{
- int startRow = Adam7.FirstRow[pass];
- int startCol = Adam7.FirstColumn[pass];
+ int startRow = Adam7.FirstRow[pass] + frameControl.YOffset;
+ int startCol = Adam7.FirstColumn[pass] + frameControl.XOffset;
int blockWidth = Adam7.ComputeBlockWidth(width, pass);
int bytesPerScanline = this.bytesPerPixel <= 1
@@ -1136,11 +1136,11 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
Span filter = filterBuffer.GetSpan();
Span attempt = attemptBuffer.GetSpan();
- for (int row = startRow; row < height; row += Adam7.RowIncrement[pass])
+ for (int row = startRow; row < frameControl.YLimit; row += Adam7.RowIncrement[pass])
{
// Collect pixel data
Span srcRow = pixelBuffer.DangerousGetRowSpan(row);
- for (int col = startCol, i = 0; col < width; col += Adam7.ColumnIncrement[pass])
+ for (int col = startCol, i = 0; col < frameControl.XLimit; col += Adam7.ColumnIncrement[pass])
{
block[i++] = srcRow[col];
}
@@ -1160,17 +1160,18 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Interlaced encoding the quantized (indexed, with palette) pixels.
///
/// The type of the pixel.
+ /// The frame control
/// The quantized.
/// The deflate stream.
- private void EncodeAdam7IndexedPixels(IndexedImageFrame quantized, ZlibDeflateStream deflateStream)
+ private void EncodeAdam7IndexedPixels(FrameControl frameControl, IndexedImageFrame quantized, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel
{
- int width = quantized.Width;
- int height = quantized.Height;
+ int width = frameControl.Width;
+ int height = frameControl.Height;
for (int pass = 0; pass < 7; pass++)
{
- int startRow = Adam7.FirstRow[pass];
- int startCol = Adam7.FirstColumn[pass];
+ int startRow = Adam7.FirstRow[pass] + frameControl.YOffset;
+ int startCol = Adam7.FirstColumn[pass] + frameControl.XOffset;
int blockWidth = Adam7.ComputeBlockWidth(width, pass);
int bytesPerScanline = this.bytesPerPixel <= 1
@@ -1190,13 +1191,13 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
Span attempt = attemptBuffer.GetSpan();
for (int row = startRow;
- row < height;
+ row < frameControl.YLimit;
row += Adam7.RowIncrement[pass])
{
// Collect data
ReadOnlySpan srcRow = quantized.DangerousGetRowSpan(row);
for (int col = startCol, i = 0;
- col < width;
+ col < frameControl.XLimit;
col += Adam7.ColumnIncrement[pass])
{
block[i] = srcRow[col];
diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
index 125aa75b75..67a1f7c82d 100644
--- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
+++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
@@ -16,7 +16,8 @@ namespace SixLabors.ImageSharp.Formats.Png;
internal static class PngScanlineProcessor
{
public static void ProcessGrayscaleScanline(
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
bool hasTrans,
@@ -24,7 +25,8 @@ internal static class PngScanlineProcessor
L8 luminanceTrans)
where TPixel : unmanaged, IPixel =>
ProcessInterlacedGrayscaleScanline(
- header,
+ bitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
0,
@@ -34,7 +36,8 @@ internal static class PngScanlineProcessor
luminanceTrans);
public static void ProcessInterlacedGrayscaleScanline(
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
uint pixelOffset,
@@ -44,17 +47,18 @@ internal static class PngScanlineProcessor
L8 luminanceTrans)
where TPixel : unmanaged, IPixel
{
+ uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
- int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(header.BitDepth) - 1);
+ int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(bitDepth) - 1);
if (!hasTrans)
{
- if (header.BitDepth == 16)
+ if (bitDepth == 16)
{
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += 2)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
pixel.FromL16(Unsafe.As(ref luminance));
@@ -63,7 +67,7 @@ internal static class PngScanlineProcessor
}
else
{
- for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++)
+ for (nuint x = offset, o = 0; x < frameControl.XLimit; x += increment, o++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
pixel.FromL8(Unsafe.As(ref luminance));
@@ -74,11 +78,11 @@ internal static class PngScanlineProcessor
return;
}
- if (header.BitDepth == 16)
+ if (bitDepth == 16)
{
La32 source = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += 2)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
source.L = luminance;
@@ -92,7 +96,7 @@ internal static class PngScanlineProcessor
{
La16 source = default;
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
- for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++)
+ for (nuint x = offset, o = 0; x < frameControl.XLimit; x += increment, o++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
source.L = luminance;
@@ -105,14 +109,16 @@ internal static class PngScanlineProcessor
}
public static void ProcessGrayscaleWithAlphaScanline(
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
uint bytesPerPixel,
uint bytesPerSample)
where TPixel : unmanaged, IPixel =>
ProcessInterlacedGrayscaleWithAlphaScanline(
- header,
+ bitDepth,
+ frameControl,
scanlineSpan,
rowSpan,
0,
@@ -121,7 +127,8 @@ internal static class PngScanlineProcessor
bytesPerSample);
public static void ProcessInterlacedGrayscaleWithAlphaScanline(
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
uint pixelOffset,
@@ -130,15 +137,16 @@ internal static class PngScanlineProcessor
uint bytesPerSample)
where TPixel : unmanaged, IPixel
{
+ uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
- if (header.BitDepth == 16)
+ if (bitDepth == 16)
{
La32 source = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += 4)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += 4)
{
source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
@@ -150,27 +158,28 @@ internal static class PngScanlineProcessor
else
{
La16 source = default;
- nuint offset = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment)
+ nuint offset2 = 0;
+ for (nuint x = offset; x < frameControl.XLimit; x += increment)
{
- source.L = Unsafe.Add(ref scanlineSpanRef, offset);
- source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
+ source.L = Unsafe.Add(ref scanlineSpanRef, offset2);
+ source.A = Unsafe.Add(ref scanlineSpanRef, offset2 + bytesPerSample);
pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
- offset += bytesPerPixel;
+ offset2 += bytesPerPixel;
}
}
}
public static void ProcessPaletteScanline(
- in PngHeader header,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
ReadOnlySpan palette,
byte[] paletteAlpha)
where TPixel : unmanaged, IPixel =>
- ProcessInterlacedPaletteScanline(header,
+ ProcessInterlacedPaletteScanline(
+ frameControl,
scanlineSpan,
rowSpan,
0,
@@ -179,7 +188,7 @@ internal static class PngScanlineProcessor
paletteAlpha);
public static void ProcessInterlacedPaletteScanline(
- in PngHeader header,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
uint pixelOffset,
@@ -193,6 +202,7 @@ internal static class PngScanlineProcessor
PngThrowHelper.ThrowMissingPalette();
}
+ uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
@@ -205,7 +215,7 @@ internal static class PngScanlineProcessor
// channel and we should try to read it.
Rgba32 rgba = default;
ref byte paletteAlphaRef = ref MemoryMarshal.GetArrayDataReference(paletteAlpha);
- for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++)
+ for (nuint x = offset, o = 0; x < frameControl.XLimit; x += increment, o++)
{
uint index = Unsafe.Add(ref scanlineSpanRef, o);
rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
@@ -217,7 +227,7 @@ internal static class PngScanlineProcessor
}
else
{
- for (nuint x = pixelOffset, o = 0; x < (uint)header.Width; x += increment, o++)
+ for (nuint x = offset, o = 0; x < frameControl.XLimit; x += increment, o++)
{
int index = Unsafe.Add(ref scanlineSpanRef, o);
Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index);
@@ -229,8 +239,8 @@ internal static class PngScanlineProcessor
}
public static void ProcessRgbScanline(
- Configuration configuration,
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
int bytesPerPixel,
@@ -240,16 +250,17 @@ internal static class PngScanlineProcessor
Rgb24 rgb24Trans)
where TPixel : unmanaged, IPixel
{
+ uint offset = (uint)frameControl.XOffset;
TPixel pixel = default;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
- if (header.BitDepth == 16)
+ if (bitDepth == 16)
{
if (!hasTrans)
{
Rgb48 rgb48 = default;
int o = 0;
- for (nuint x = 0; x < (uint)header.Width; x++, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x++, o += bytesPerPixel)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@@ -264,7 +275,7 @@ internal static class PngScanlineProcessor
Rgb48 rgb48 = default;
Rgba64 rgba64 = default;
int o = 0;
- for (nuint x = 0; x < (uint)header.Width; x++, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x++, o += bytesPerPixel)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@@ -286,7 +297,7 @@ internal static class PngScanlineProcessor
Rgba32 rgba32 = default;
ReadOnlySpan rgb24Span = MemoryMarshal.Cast(scanlineSpan);
ref Rgb24 rgb24SpanRef = ref MemoryMarshal.GetReference(rgb24Span);
- for (nuint x = 0; x < (uint)header.Width; x++)
+ for (nuint x = offset; x < frameControl.XLimit; x++)
{
ref readonly Rgb24 rgb24 = ref Unsafe.Add(ref rgb24SpanRef, x);
rgba32.Rgb = rgb24;
@@ -298,12 +309,23 @@ internal static class PngScanlineProcessor
}
else
{
- PixelOperations.Instance.FromRgb24Bytes(configuration, scanlineSpan, rowSpan, header.Width);
+ ReadOnlySpan source = MemoryMarshal.Cast(scanlineSpan)[..frameControl.Width];
+ ref Rgb24 sourceBaseRef = ref MemoryMarshal.GetReference(source);
+ ref TPixel destBaseRef = ref MemoryMarshal.GetReference(rowSpan);
+
+ for (nuint i = offset; i < frameControl.XLimit; i++)
+ {
+ ref Rgb24 sp = ref Unsafe.Add(ref sourceBaseRef, i);
+ ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i);
+
+ dp.FromRgb24(sp);
+ }
}
}
public static void ProcessInterlacedRgbScanline(
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
uint pixelOffset,
@@ -315,18 +337,19 @@ internal static class PngScanlineProcessor
Rgb24 rgb24Trans)
where TPixel : unmanaged, IPixel
{
+ uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
- if (header.BitDepth == 16)
+ if (bitDepth == 16)
{
if (hasTrans)
{
Rgb48 rgb48 = default;
Rgba64 rgba64 = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += bytesPerPixel)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@@ -343,7 +366,7 @@ internal static class PngScanlineProcessor
{
Rgb48 rgb48 = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += bytesPerPixel)
{
rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@@ -361,7 +384,7 @@ internal static class PngScanlineProcessor
{
Rgba32 rgba = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += bytesPerPixel)
{
rgba.R = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));
@@ -376,7 +399,7 @@ internal static class PngScanlineProcessor
{
Rgb24 rgb = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += bytesPerPixel)
{
rgb.R = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgb.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));
@@ -389,22 +412,23 @@ internal static class PngScanlineProcessor
}
public static void ProcessRgbaScanline(
- Configuration configuration,
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
int bytesPerPixel,
int bytesPerSample)
where TPixel : unmanaged, IPixel
{
+ uint offset = (uint)frameControl.XOffset;
TPixel pixel = default;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
- if (header.BitDepth == 16)
+ if (bitDepth == 16)
{
Rgba64 rgba64 = default;
int o = 0;
- for (nuint x = 0; x < (uint)header.Width; x++, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x++, o += bytesPerPixel)
{
rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@@ -417,12 +441,23 @@ internal static class PngScanlineProcessor
}
else
{
- PixelOperations.Instance.FromRgba32Bytes(configuration, scanlineSpan, rowSpan, header.Width);
+ ReadOnlySpan source = MemoryMarshal.Cast(scanlineSpan)[..frameControl.Width];
+ ref Rgba32 sourceBaseRef = ref MemoryMarshal.GetReference(source);
+ ref TPixel destBaseRef = ref MemoryMarshal.GetReference(rowSpan);
+
+ for (nuint i = offset; i < frameControl.XLimit; i++)
+ {
+ ref Rgba32 sp = ref Unsafe.Add(ref sourceBaseRef, i);
+ ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i);
+
+ dp.FromRgba32(sp);
+ }
}
}
public static void ProcessInterlacedRgbaScanline(
- in PngHeader header,
+ int bitDepth,
+ FrameControl frameControl,
ReadOnlySpan scanlineSpan,
Span rowSpan,
uint pixelOffset,
@@ -431,15 +466,16 @@ internal static class PngScanlineProcessor
int bytesPerSample)
where TPixel : unmanaged, IPixel
{
+ uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
- if (header.BitDepth == 16)
+ if (bitDepth == 16)
{
Rgba64 rgba64 = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += bytesPerPixel)
{
rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@@ -454,7 +490,7 @@ internal static class PngScanlineProcessor
{
Rgba32 rgba = default;
int o = 0;
- for (nuint x = pixelOffset; x < (uint)header.Width; x += increment, o += bytesPerPixel)
+ for (nuint x = offset; x < frameControl.XLimit; x += increment, o += bytesPerPixel)
{
rgba.R = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));