Browse Source

Fix offset

pull/2511/head
Poker 3 years ago
parent
commit
c253f39a32
No known key found for this signature in database GPG Key ID: 720AFAD63099D9CB
  1. 18
      src/ImageSharp/Formats/Png/Chunks/FrameControl.cs
  2. 73
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 71
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  4. 130
      src/ImageSharp/Formats/Png/PngScanlineProcessor.cs

18
src/ImageSharp/Formats/Png/Chunks/FrameControl.cs

@ -56,6 +56,16 @@ internal readonly struct FrameControl
/// </summary> /// </summary>
public int YOffset { get; } public int YOffset { get; }
/// <summary>
/// Gets the X limit at which to render the following frame
/// </summary>
public uint XLimit => (uint)(this.XOffset + this.Width);
/// <summary>
/// Gets the Y limit at which to render the following frame
/// </summary>
public uint YLimit => (uint)(this.YOffset + this.Height);
/// <summary> /// <summary>
/// Gets the frame delay fraction numerator /// Gets the frame delay fraction numerator
/// </summary> /// </summary>
@ -104,14 +114,14 @@ internal readonly struct FrameControl
PngThrowHelper.ThrowInvalidParameter(this.Height, "Expected > 0"); 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)}");
} }
} }

73
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -228,6 +228,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.currentStream.Position += 4; // Skip sequence number this.currentStream.Position += 4; // Skip sequence number
return length - 4; return length - 4;
}, },
lastFrameControl.Value,
cancellationToken); cancellationToken);
lastFrameControl = null; lastFrameControl = null;
break; break;
@ -237,7 +238,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
this.InitializeImage(metadata, lastFrameControl, out image); 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; lastFrameControl = null;
break; break;
case PngChunkType.Palette: case PngChunkType.Palette:
@ -682,8 +685,9 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <param name="image"> The pixel data.</param> /// <param name="image"> The pixel data.</param>
/// <param name="pngMetadata">The png metadata</param> /// <param name="pngMetadata">The png metadata</param>
/// <param name="getData">A delegate to get more data from the inner stream for <see cref="ZlibInflateStream"/>.</param> /// <param name="getData">A delegate to get more data from the inner stream for <see cref="ZlibInflateStream"/>.</param>
/// <param name="frameControl">The frame control</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
private void ReadScanlines<TPixel>(int chunkLength, ImageFrame<TPixel> image, PngMetadata pngMetadata, Func<int> getData, CancellationToken cancellationToken) private void ReadScanlines<TPixel>(int chunkLength, ImageFrame<TPixel> image, PngMetadata pngMetadata, Func<int> getData, FrameControl frameControl, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using ZlibInflateStream deframeStream = new(this.currentStream, getData); using ZlibInflateStream deframeStream = new(this.currentStream, getData);
@ -692,11 +696,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
if (this.header.InterlaceMethod is PngInterlaceMode.Adam7) if (this.header.InterlaceMethod is PngInterlaceMode.Adam7)
{ {
this.DecodeInterlacedPixelData(dataStream, image, pngMetadata, cancellationToken); this.DecodeInterlacedPixelData(frameControl, dataStream, image, pngMetadata, cancellationToken);
} }
else 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 /// Decodes the raw pixel data row by row
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The image to decode to.</param> /// <param name="image">The image to decode to.</param>
/// <param name="pngMetadata">The png metadata</param> /// <param name="pngMetadata">The png metadata</param>
/// <param name="cancellationToken">The CancellationToken</param> /// <param name="cancellationToken">The CancellationToken</param>
private void DecodePixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken) private void DecodePixelData<TPixel>(FrameControl frameControl, DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int currentRow = Adam7.FirstRow[0]; int currentRow = frameControl.YOffset;
int currentRowBytesRead = 0; int currentRowBytesRead = 0;
int height = image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata) ? frameMetadata.Height : this.header.Height; int height = frameControl.Height;
while (currentRow < height) while (currentRow < height)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -757,7 +762,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
break; break;
} }
this.ProcessDefilteredScanline(currentRow, scanlineSpan, image, pngMetadata); this.ProcessDefilteredScanline(frameControl, currentRow, scanlineSpan, image, pngMetadata);
this.SwapScanlineBuffers(); this.SwapScanlineBuffers();
currentRow++; currentRow++;
@ -769,23 +774,19 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/> /// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/>
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The current image.</param> /// <param name="image">The current image.</param>
/// <param name="pngMetadata">The png metadata.</param> /// <param name="pngMetadata">The png metadata.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
private void DecodeInterlacedPixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken) private void DecodeInterlacedPixelData<TPixel>(FrameControl frameControl, DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int currentRow = Adam7.FirstRow[0]; int currentRow = Adam7.FirstRow[0] + frameControl.YOffset;
int currentRowBytesRead = 0; int currentRowBytesRead = 0;
int pass = 0; int pass = 0;
int width = this.header.Width; int width = frameControl.Width;
int height = this.header.Height; int height = frameControl.Height;
if (image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata))
{
width = frameMetadata.Width;
height = frameMetadata.Height;
}
Buffer2D<TPixel> imageBuffer = image.PixelBuffer; Buffer2D<TPixel> imageBuffer = image.PixelBuffer;
while (true) while (true)
@ -848,7 +849,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
} }
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(currentRow); Span<TPixel> 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(); this.SwapScanlineBuffers();
@ -874,11 +875,12 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// Processes the de-filtered scanline filling the image pixel data /// Processes the de-filtered scanline filling the image pixel data
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="currentRow">The index of the current scanline being processed.</param> /// <param name="currentRow">The index of the current scanline being processed.</param>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image</param> /// <param name="pixels">The image</param>
/// <param name="pngMetadata">The png metadata.</param> /// <param name="pngMetadata">The png metadata.</param>
private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetadata pngMetadata) private void ProcessDefilteredScanline<TPixel>(FrameControl frameControl, int currentRow, ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetadata pngMetadata)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Span<TPixel> rowSpan = pixels.PixelBuffer.DangerousGetRowSpan(currentRow); Span<TPixel> rowSpan = pixels.PixelBuffer.DangerousGetRowSpan(currentRow);
@ -902,7 +904,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
PngScanlineProcessor.ProcessGrayscaleScanline( PngScanlineProcessor.ProcessGrayscaleScanline(
this.header, this.header.BitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
pngMetadata.HasTransparency, pngMetadata.HasTransparency,
@ -913,7 +916,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline( PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline(
this.header, this.header.BitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
(uint)this.bytesPerPixel, (uint)this.bytesPerPixel,
@ -923,7 +927,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Palette: case PngColorType.Palette:
PngScanlineProcessor.ProcessPaletteScanline( PngScanlineProcessor.ProcessPaletteScanline(
this.header, frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
this.palette, this.palette,
@ -933,8 +937,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Rgb: case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline( PngScanlineProcessor.ProcessRgbScanline(
this.configuration, this.header.BitDepth,
this.header, frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
this.bytesPerPixel, this.bytesPerPixel,
@ -947,8 +951,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline( PngScanlineProcessor.ProcessRgbaScanline(
this.configuration, this.header.BitDepth,
this.header, frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
this.bytesPerPixel, this.bytesPerPixel,
@ -967,12 +971,13 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// Processes the interlaced de-filtered scanline filling the image pixel data /// Processes the interlaced de-filtered scanline filling the image pixel data
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="rowSpan">The current image row.</param> /// <param name="rowSpan">The current image row.</param>
/// <param name="pngMetadata">The png metadata.</param> /// <param name="pngMetadata">The png metadata.</param>
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param> /// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param> /// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1) private void ProcessInterlacedDefilteredScanline<TPixel>(FrameControl frameControl, ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// Trim the first marker byte from the buffer // Trim the first marker byte from the buffer
@ -994,7 +999,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
PngScanlineProcessor.ProcessInterlacedGrayscaleScanline( PngScanlineProcessor.ProcessInterlacedGrayscaleScanline(
this.header, this.header.BitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
(uint)pixelOffset, (uint)pixelOffset,
@ -1007,7 +1013,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline( PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline(
this.header, this.header.BitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
(uint)pixelOffset, (uint)pixelOffset,
@ -1019,7 +1026,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Palette: case PngColorType.Palette:
PngScanlineProcessor.ProcessInterlacedPaletteScanline( PngScanlineProcessor.ProcessInterlacedPaletteScanline(
this.header, frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
(uint)pixelOffset, (uint)pixelOffset,
@ -1031,7 +1038,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.Rgb: case PngColorType.Rgb:
PngScanlineProcessor.ProcessInterlacedRgbScanline( PngScanlineProcessor.ProcessInterlacedRgbScanline(
this.header, this.header.BitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
(uint)pixelOffset, (uint)pixelOffset,
@ -1046,7 +1054,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
case PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessInterlacedRgbaScanline( PngScanlineProcessor.ProcessInterlacedRgbaScanline(
this.header, this.header.BitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
(uint)pixelOffset, (uint)pixelOffset,

71
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.WriteAnimationControlChunk(stream, targetImage.Frames.Count, pngMetadata.NumberPlays);
this.WriteFrameControlChunk(stream, targetImage.Frames.RootFrame.Metadata.GetPngFrameMetadata(), 0); FrameControl frameControl = this.WriteFrameControlChunk(stream, targetImage.Frames.RootFrame.Metadata.GetPngFrameMetadata(), 0);
_ = this.WriteDataChunks(targetImage.Frames.RootFrame, rootQuantized, stream, false); _ = this.WriteDataChunks(frameControl, targetImage.Frames.RootFrame, rootQuantized, stream, false);
int index = 1; int index = 1;
foreach (ImageFrame<TPixel> imageFrame in ((IEnumerable<ImageFrame<TPixel>>)targetImage.Frames).Skip(1)) foreach (ImageFrame<TPixel> imageFrame in ((IEnumerable<ImageFrame<TPixel>>)targetImage.Frames).Skip(1))
{ {
this.WriteFrameControlChunk(stream, imageFrame.Metadata.GetPngFrameMetadata(), index); frameControl = this.WriteFrameControlChunk(stream, imageFrame.Metadata.GetPngFrameMetadata(), index);
index++; index++;
IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedImageAndUpdateBitDepth(imageFrame); IndexedImageFrame<TPixel>? quantized = this.CreateQuantizedImageAndUpdateBitDepth(imageFrame);
index += this.WriteDataChunks(imageFrame, quantized, stream, true, index); index += this.WriteDataChunks(frameControl, imageFrame, quantized, stream, true);
quantized?.Dispose(); quantized?.Dispose();
} }
} }
else 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(); rootQuantized?.Dispose();
} }
@ -962,25 +963,27 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="frameMetadata">Provides APng specific metadata information for the image frame.</param> /// <param name="frameMetadata">Provides APng specific metadata information for the image frame.</param>
/// <param name="sequenceNumber">Sequence number.</param> /// <param name="sequenceNumber">Sequence number.</param>
private void WriteFrameControlChunk(Stream stream, PngFrameMetadata frameMetadata, int sequenceNumber) private FrameControl WriteFrameControlChunk(Stream stream, PngFrameMetadata frameMetadata, int sequenceNumber)
{ {
FrameControl fcTL = FrameControl.FromMetadata(frameMetadata, sequenceNumber); FrameControl fcTL = FrameControl.FromMetadata(frameMetadata, sequenceNumber);
fcTL.WriteTo(this.chunkDataBuffer.Span); fcTL.WriteTo(this.chunkDataBuffer.Span);
this.WriteChunk(stream, PngChunkType.FrameControl, this.chunkDataBuffer.Span, 0, FrameControl.Size); this.WriteChunk(stream, PngChunkType.FrameControl, this.chunkDataBuffer.Span, 0, FrameControl.Size);
return fcTL;
} }
/// <summary> /// <summary>
/// Writes the pixel information to the stream. /// Writes the pixel information to the stream.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="pixels">The frame.</param> /// <param name="pixels">The frame.</param>
/// <param name="quantized">The quantized pixel data. Can be null.</param> /// <param name="quantized">The quantized pixel data. Can be null.</param>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
/// <param name="isFrame">Is writing fdAT or IDAT.</param> /// <param name="isFrame">Is writing fdAT or IDAT.</param>
/// <param name="startSequenceNumber">Start sequence number.</param> private int WriteDataChunks<TPixel>(FrameControl frameControl, ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, Stream stream, bool isFrame)
private int WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, Stream stream, bool isFrame, int startSequenceNumber = 0)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
byte[] buffer; byte[] buffer;
@ -994,16 +997,16 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
{ {
if (quantized is not null) if (quantized is not null)
{ {
this.EncodeAdam7IndexedPixels(quantized, deflateStream); this.EncodeAdam7IndexedPixels(frameControl, quantized, deflateStream);
} }
else else
{ {
this.EncodeAdam7Pixels(pixels, deflateStream); this.EncodeAdam7Pixels(frameControl, pixels, deflateStream);
} }
} }
else else
{ {
this.EncodePixels(pixels, quantized, deflateStream); this.EncodePixels(frameControl, pixels, quantized, deflateStream);
} }
} }
@ -1038,7 +1041,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
if (isFrame) if (isFrame)
{ {
byte[] chunkBuffer = new byte[MaxBlockSize]; 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)); buffer.AsSpan().Slice(i * maxBlockSize, length).CopyTo(chunkBuffer.AsSpan(4, length));
this.WriteChunk(stream, PngChunkType.FrameData, chunkBuffer, 0, length + 4); this.WriteChunk(stream, PngChunkType.FrameData, chunkBuffer, 0, length + 4);
@ -1069,19 +1072,15 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Encodes the pixels. /// Encodes the pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="pixels">The pixels.</param> /// <param name="pixels">The pixels.</param>
/// <param name="quantized">The quantized pixels span.</param> /// <param name="quantized">The quantized pixels span.</param>
/// <param name="deflateStream">The deflate stream.</param> /// <param name="deflateStream">The deflate stream.</param>
private void EncodePixels<TPixel>(ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, ZlibDeflateStream deflateStream) private void EncodePixels<TPixel>(FrameControl frameControl, ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int width = this.width; int width = frameControl.Width;
int height = this.height; int height = frameControl.Height;
if (pixels.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? pngMetadata))
{
width = pngMetadata.Width;
height = pngMetadata.Height;
}
int bytesPerScanline = this.CalculateScanlineLength(width); int bytesPerScanline = this.CalculateScanlineLength(width);
int filterLength = bytesPerScanline + 1; int filterLength = bytesPerScanline + 1;
@ -1094,7 +1093,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
{ {
Span<byte> filter = filterBuffer.GetSpan(); Span<byte> filter = filterBuffer.GetSpan();
Span<byte> attempt = attemptBuffer.GetSpan(); Span<byte> 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); this.CollectAndFilterPixelRow(accessor.GetRowSpan(y), ref filter, ref attempt, quantized, y);
deflateStream.Write(filter); deflateStream.Write(filter);
@ -1107,18 +1106,19 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Interlaced encoding the pixels. /// Interlaced encoding the pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="frame">The image frame.</param> /// <param name="frame">The image frame.</param>
/// <param name="deflateStream">The deflate stream.</param> /// <param name="deflateStream">The deflate stream.</param>
private void EncodeAdam7Pixels<TPixel>(ImageFrame<TPixel> frame, ZlibDeflateStream deflateStream) private void EncodeAdam7Pixels<TPixel>(FrameControl frameControl, ImageFrame<TPixel> frame, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int width = frame.Width; int width = frameControl.Width;
int height = frame.Height; int height = frameControl.Height;
Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer; Buffer2D<TPixel> pixelBuffer = frame.PixelBuffer;
for (int pass = 0; pass < 7; pass++) for (int pass = 0; pass < 7; pass++)
{ {
int startRow = Adam7.FirstRow[pass]; int startRow = Adam7.FirstRow[pass] + frameControl.YOffset;
int startCol = Adam7.FirstColumn[pass]; int startCol = Adam7.FirstColumn[pass] + frameControl.XOffset;
int blockWidth = Adam7.ComputeBlockWidth(width, pass); int blockWidth = Adam7.ComputeBlockWidth(width, pass);
int bytesPerScanline = this.bytesPerPixel <= 1 int bytesPerScanline = this.bytesPerPixel <= 1
@ -1136,11 +1136,11 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
Span<byte> filter = filterBuffer.GetSpan(); Span<byte> filter = filterBuffer.GetSpan();
Span<byte> attempt = attemptBuffer.GetSpan(); Span<byte> 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 // Collect pixel data
Span<TPixel> srcRow = pixelBuffer.DangerousGetRowSpan(row); Span<TPixel> 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]; block[i++] = srcRow[col];
} }
@ -1160,17 +1160,18 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Interlaced encoding the quantized (indexed, with palette) pixels. /// Interlaced encoding the quantized (indexed, with palette) pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="quantized">The quantized.</param> /// <param name="quantized">The quantized.</param>
/// <param name="deflateStream">The deflate stream.</param> /// <param name="deflateStream">The deflate stream.</param>
private void EncodeAdam7IndexedPixels<TPixel>(IndexedImageFrame<TPixel> quantized, ZlibDeflateStream deflateStream) private void EncodeAdam7IndexedPixels<TPixel>(FrameControl frameControl, IndexedImageFrame<TPixel> quantized, ZlibDeflateStream deflateStream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int width = quantized.Width; int width = frameControl.Width;
int height = quantized.Height; int height = frameControl.Height;
for (int pass = 0; pass < 7; pass++) for (int pass = 0; pass < 7; pass++)
{ {
int startRow = Adam7.FirstRow[pass]; int startRow = Adam7.FirstRow[pass] + frameControl.YOffset;
int startCol = Adam7.FirstColumn[pass]; int startCol = Adam7.FirstColumn[pass] + frameControl.XOffset;
int blockWidth = Adam7.ComputeBlockWidth(width, pass); int blockWidth = Adam7.ComputeBlockWidth(width, pass);
int bytesPerScanline = this.bytesPerPixel <= 1 int bytesPerScanline = this.bytesPerPixel <= 1
@ -1190,13 +1191,13 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
Span<byte> attempt = attemptBuffer.GetSpan(); Span<byte> attempt = attemptBuffer.GetSpan();
for (int row = startRow; for (int row = startRow;
row < height; row < frameControl.YLimit;
row += Adam7.RowIncrement[pass]) row += Adam7.RowIncrement[pass])
{ {
// Collect data // Collect data
ReadOnlySpan<byte> srcRow = quantized.DangerousGetRowSpan(row); ReadOnlySpan<byte> srcRow = quantized.DangerousGetRowSpan(row);
for (int col = startCol, i = 0; for (int col = startCol, i = 0;
col < width; col < frameControl.XLimit;
col += Adam7.ColumnIncrement[pass]) col += Adam7.ColumnIncrement[pass])
{ {
block[i] = srcRow[col]; block[i] = srcRow[col];

130
src/ImageSharp/Formats/Png/PngScanlineProcessor.cs

@ -16,7 +16,8 @@ namespace SixLabors.ImageSharp.Formats.Png;
internal static class PngScanlineProcessor internal static class PngScanlineProcessor
{ {
public static void ProcessGrayscaleScanline<TPixel>( public static void ProcessGrayscaleScanline<TPixel>(
in PngHeader header, int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
bool hasTrans, bool hasTrans,
@ -24,7 +25,8 @@ internal static class PngScanlineProcessor
L8 luminanceTrans) L8 luminanceTrans)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
ProcessInterlacedGrayscaleScanline( ProcessInterlacedGrayscaleScanline(
header, bitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
0, 0,
@ -34,7 +36,8 @@ internal static class PngScanlineProcessor
luminanceTrans); luminanceTrans);
public static void ProcessInterlacedGrayscaleScanline<TPixel>( public static void ProcessInterlacedGrayscaleScanline<TPixel>(
in PngHeader header, int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
uint pixelOffset, uint pixelOffset,
@ -44,17 +47,18 @@ internal static class PngScanlineProcessor
L8 luminanceTrans) L8 luminanceTrans)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default; TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); 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 (!hasTrans)
{ {
if (header.BitDepth == 16) if (bitDepth == 16)
{ {
int o = 0; 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)); ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
pixel.FromL16(Unsafe.As<ushort, L16>(ref luminance)); pixel.FromL16(Unsafe.As<ushort, L16>(ref luminance));
@ -63,7 +67,7 @@ internal static class PngScanlineProcessor
} }
else 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); byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
pixel.FromL8(Unsafe.As<byte, L8>(ref luminance)); pixel.FromL8(Unsafe.As<byte, L8>(ref luminance));
@ -74,11 +78,11 @@ internal static class PngScanlineProcessor
return; return;
} }
if (header.BitDepth == 16) if (bitDepth == 16)
{ {
La32 source = default; La32 source = default;
int o = 0; 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)); ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
source.L = luminance; source.L = luminance;
@ -92,7 +96,7 @@ internal static class PngScanlineProcessor
{ {
La16 source = default; La16 source = default;
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); 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); byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
source.L = luminance; source.L = luminance;
@ -105,14 +109,16 @@ internal static class PngScanlineProcessor
} }
public static void ProcessGrayscaleWithAlphaScanline<TPixel>( public static void ProcessGrayscaleWithAlphaScanline<TPixel>(
in PngHeader header, int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
uint bytesPerPixel, uint bytesPerPixel,
uint bytesPerSample) uint bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
ProcessInterlacedGrayscaleWithAlphaScanline( ProcessInterlacedGrayscaleWithAlphaScanline(
header, bitDepth,
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
0, 0,
@ -121,7 +127,8 @@ internal static class PngScanlineProcessor
bytesPerSample); bytesPerSample);
public static void ProcessInterlacedGrayscaleWithAlphaScanline<TPixel>( public static void ProcessInterlacedGrayscaleWithAlphaScanline<TPixel>(
in PngHeader header, int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
uint pixelOffset, uint pixelOffset,
@ -130,15 +137,16 @@ internal static class PngScanlineProcessor
uint bytesPerSample) uint bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default; TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (header.BitDepth == 16) if (bitDepth == 16)
{ {
La32 source = default; La32 source = default;
int o = 0; 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.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
@ -150,27 +158,28 @@ internal static class PngScanlineProcessor
else else
{ {
La16 source = default; La16 source = default;
nuint offset = 0; nuint offset2 = 0;
for (nuint x = pixelOffset; x < (uint)header.Width; x += increment) for (nuint x = offset; x < frameControl.XLimit; x += increment)
{ {
source.L = Unsafe.Add(ref scanlineSpanRef, offset); source.L = Unsafe.Add(ref scanlineSpanRef, offset2);
source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); source.A = Unsafe.Add(ref scanlineSpanRef, offset2 + bytesPerSample);
pixel.FromLa16(source); pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel; Unsafe.Add(ref rowSpanRef, x) = pixel;
offset += bytesPerPixel; offset2 += bytesPerPixel;
} }
} }
} }
public static void ProcessPaletteScanline<TPixel>( public static void ProcessPaletteScanline<TPixel>(
in PngHeader header, FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
ReadOnlySpan<byte> palette, ReadOnlySpan<byte> palette,
byte[] paletteAlpha) byte[] paletteAlpha)
where TPixel : unmanaged, IPixel<TPixel> => where TPixel : unmanaged, IPixel<TPixel> =>
ProcessInterlacedPaletteScanline(header, ProcessInterlacedPaletteScanline(
frameControl,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
0, 0,
@ -179,7 +188,7 @@ internal static class PngScanlineProcessor
paletteAlpha); paletteAlpha);
public static void ProcessInterlacedPaletteScanline<TPixel>( public static void ProcessInterlacedPaletteScanline<TPixel>(
in PngHeader header, FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
uint pixelOffset, uint pixelOffset,
@ -193,6 +202,7 @@ internal static class PngScanlineProcessor
PngThrowHelper.ThrowMissingPalette(); PngThrowHelper.ThrowMissingPalette();
} }
uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default; TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
@ -205,7 +215,7 @@ internal static class PngScanlineProcessor
// channel and we should try to read it. // channel and we should try to read it.
Rgba32 rgba = default; Rgba32 rgba = default;
ref byte paletteAlphaRef = ref MemoryMarshal.GetArrayDataReference(paletteAlpha); 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); uint index = Unsafe.Add(ref scanlineSpanRef, o);
rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue; rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue;
@ -217,7 +227,7 @@ internal static class PngScanlineProcessor
} }
else 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); int index = Unsafe.Add(ref scanlineSpanRef, o);
Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index); Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index);
@ -229,8 +239,8 @@ internal static class PngScanlineProcessor
} }
public static void ProcessRgbScanline<TPixel>( public static void ProcessRgbScanline<TPixel>(
Configuration configuration, int bitDepth,
in PngHeader header, FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
int bytesPerPixel, int bytesPerPixel,
@ -240,16 +250,17 @@ internal static class PngScanlineProcessor
Rgb24 rgb24Trans) Rgb24 rgb24Trans)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = (uint)frameControl.XOffset;
TPixel pixel = default; TPixel pixel = default;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (header.BitDepth == 16) if (bitDepth == 16)
{ {
if (!hasTrans) if (!hasTrans)
{ {
Rgb48 rgb48 = default; Rgb48 rgb48 = default;
int o = 0; 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.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@ -264,7 +275,7 @@ internal static class PngScanlineProcessor
Rgb48 rgb48 = default; Rgb48 rgb48 = default;
Rgba64 rgba64 = default; Rgba64 rgba64 = default;
int o = 0; 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.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@ -286,7 +297,7 @@ internal static class PngScanlineProcessor
Rgba32 rgba32 = default; Rgba32 rgba32 = default;
ReadOnlySpan<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(scanlineSpan); ReadOnlySpan<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(scanlineSpan);
ref Rgb24 rgb24SpanRef = ref MemoryMarshal.GetReference(rgb24Span); 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); ref readonly Rgb24 rgb24 = ref Unsafe.Add(ref rgb24SpanRef, x);
rgba32.Rgb = rgb24; rgba32.Rgb = rgb24;
@ -298,12 +309,23 @@ internal static class PngScanlineProcessor
} }
else else
{ {
PixelOperations<TPixel>.Instance.FromRgb24Bytes(configuration, scanlineSpan, rowSpan, header.Width); ReadOnlySpan<Rgb24> source = MemoryMarshal.Cast<byte, Rgb24>(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<TPixel>( public static void ProcessInterlacedRgbScanline<TPixel>(
in PngHeader header, int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
uint pixelOffset, uint pixelOffset,
@ -315,18 +337,19 @@ internal static class PngScanlineProcessor
Rgb24 rgb24Trans) Rgb24 rgb24Trans)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default; TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (header.BitDepth == 16) if (bitDepth == 16)
{ {
if (hasTrans) if (hasTrans)
{ {
Rgb48 rgb48 = default; Rgb48 rgb48 = default;
Rgba64 rgba64 = default; Rgba64 rgba64 = default;
int o = 0; 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.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@ -343,7 +366,7 @@ internal static class PngScanlineProcessor
{ {
Rgb48 rgb48 = default; Rgb48 rgb48 = default;
int o = 0; 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.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@ -361,7 +384,7 @@ internal static class PngScanlineProcessor
{ {
Rgba32 rgba = default; Rgba32 rgba = default;
int o = 0; 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.R = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));
@ -376,7 +399,7 @@ internal static class PngScanlineProcessor
{ {
Rgb24 rgb = default; Rgb24 rgb = default;
int o = 0; 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.R = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgb.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); rgb.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));
@ -389,22 +412,23 @@ internal static class PngScanlineProcessor
} }
public static void ProcessRgbaScanline<TPixel>( public static void ProcessRgbaScanline<TPixel>(
Configuration configuration, int bitDepth,
in PngHeader header, FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
int bytesPerPixel, int bytesPerPixel,
int bytesPerSample) int bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = (uint)frameControl.XOffset;
TPixel pixel = default; TPixel pixel = default;
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (header.BitDepth == 16) if (bitDepth == 16)
{ {
Rgba64 rgba64 = default; Rgba64 rgba64 = default;
int o = 0; 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.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@ -417,12 +441,23 @@ internal static class PngScanlineProcessor
} }
else else
{ {
PixelOperations<TPixel>.Instance.FromRgba32Bytes(configuration, scanlineSpan, rowSpan, header.Width); ReadOnlySpan<Rgba32> source = MemoryMarshal.Cast<byte, Rgba32>(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<TPixel>( public static void ProcessInterlacedRgbaScanline<TPixel>(
in PngHeader header, int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan, ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan, Span<TPixel> rowSpan,
uint pixelOffset, uint pixelOffset,
@ -431,15 +466,16 @@ internal static class PngScanlineProcessor
int bytesPerSample) int bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
uint offset = pixelOffset + (uint)frameControl.XOffset;
TPixel pixel = default; TPixel pixel = default;
ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan);
ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan);
if (header.BitDepth == 16) if (bitDepth == 16)
{ {
Rgba64 rgba64 = default; Rgba64 rgba64 = default;
int o = 0; 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.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample));
rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample));
@ -454,7 +490,7 @@ internal static class PngScanlineProcessor
{ {
Rgba32 rgba = default; Rgba32 rgba = default;
int o = 0; 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.R = Unsafe.Add(ref scanlineSpanRef, (uint)o);
rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample)); rgba.G = Unsafe.Add(ref scanlineSpanRef, (uint)(o + bytesPerSample));

Loading…
Cancel
Save