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>
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>
/// Gets the frame delay fraction numerator
/// </summary>
@ -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)}");
}
}

73
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
/// <param name="image"> The pixel data.</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="frameControl">The frame control</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>
{
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
/// </summary>
/// <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="image">The image to decode to.</param>
/// <param name="pngMetadata">The png metadata</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>
{
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
/// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/>
/// </summary>
/// <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="image">The current image.</param>
/// <param name="pngMetadata">The png metadata.</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>
{
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<TPixel> imageBuffer = image.PixelBuffer;
while (true)
@ -848,7 +849,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
}
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();
@ -874,11 +875,12 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
/// Processes the de-filtered scanline filling the image pixel data
/// </summary>
/// <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="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image</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>
{
Span<TPixel> 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
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="rowSpan">The current image row.</param>
/// <param name="pngMetadata">The png metadata.</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>
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>
{
// 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,

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.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<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++;
IndexedImageFrame<TPixel>? 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
/// <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="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);
fcTL.WriteTo(this.chunkDataBuffer.Span);
this.WriteChunk(stream, PngChunkType.FrameControl, this.chunkDataBuffer.Span, 0, FrameControl.Size);
return fcTL;
}
/// <summary>
/// Writes the pixel information to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="pixels">The frame.</param>
/// <param name="quantized">The quantized pixel data. Can be null.</param>
/// <param name="stream">The stream.</param>
/// <param name="isFrame">Is writing fdAT or IDAT.</param>
/// <param name="startSequenceNumber">Start sequence number.</param>
private int WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, Stream stream, bool isFrame, int startSequenceNumber = 0)
private int WriteDataChunks<TPixel>(FrameControl frameControl, ImageFrame<TPixel> pixels, IndexedImageFrame<TPixel>? quantized, Stream stream, bool isFrame)
where TPixel : unmanaged, IPixel<TPixel>
{
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.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="pixels">The pixels.</param>
/// <param name="quantized">The quantized pixels span.</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>
{
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<byte> filter = filterBuffer.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);
deflateStream.Write(filter);
@ -1107,18 +1106,19 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Interlaced encoding the pixels.
/// </summary>
/// <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="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>
{
int width = frame.Width;
int height = frame.Height;
int width = frameControl.Width;
int height = frameControl.Height;
Buffer2D<TPixel> 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<byte> filter = filterBuffer.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
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];
}
@ -1160,17 +1160,18 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// Interlaced encoding the quantized (indexed, with palette) pixels.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frameControl">The frame control</param>
/// <param name="quantized">The quantized.</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>
{
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<byte> attempt = attemptBuffer.GetSpan();
for (int row = startRow;
row < height;
row < frameControl.YLimit;
row += Adam7.RowIncrement[pass])
{
// Collect data
ReadOnlySpan<byte> srcRow = quantized.DangerousGetRowSpan(row);
for (int col = startCol, i = 0;
col < width;
col < frameControl.XLimit;
col += Adam7.ColumnIncrement[pass])
{
block[i] = srcRow[col];

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

@ -16,7 +16,8 @@ namespace SixLabors.ImageSharp.Formats.Png;
internal static class PngScanlineProcessor
{
public static void ProcessGrayscaleScanline<TPixel>(
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
bool hasTrans,
@ -24,7 +25,8 @@ internal static class PngScanlineProcessor
L8 luminanceTrans)
where TPixel : unmanaged, IPixel<TPixel> =>
ProcessInterlacedGrayscaleScanline(
header,
bitDepth,
frameControl,
scanlineSpan,
rowSpan,
0,
@ -34,7 +36,8 @@ internal static class PngScanlineProcessor
luminanceTrans);
public static void ProcessInterlacedGrayscaleScanline<TPixel>(
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
uint pixelOffset,
@ -44,17 +47,18 @@ internal static class PngScanlineProcessor
L8 luminanceTrans)
where TPixel : unmanaged, IPixel<TPixel>
{
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<ushort, L16>(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<byte, L8>(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<TPixel>(
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
uint bytesPerPixel,
uint bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel> =>
ProcessInterlacedGrayscaleWithAlphaScanline(
header,
bitDepth,
frameControl,
scanlineSpan,
rowSpan,
0,
@ -121,7 +127,8 @@ internal static class PngScanlineProcessor
bytesPerSample);
public static void ProcessInterlacedGrayscaleWithAlphaScanline<TPixel>(
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
uint pixelOffset,
@ -130,15 +137,16 @@ internal static class PngScanlineProcessor
uint bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel>
{
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<TPixel>(
in PngHeader header,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
ReadOnlySpan<byte> palette,
byte[] paletteAlpha)
where TPixel : unmanaged, IPixel<TPixel> =>
ProcessInterlacedPaletteScanline(header,
ProcessInterlacedPaletteScanline(
frameControl,
scanlineSpan,
rowSpan,
0,
@ -179,7 +188,7 @@ internal static class PngScanlineProcessor
paletteAlpha);
public static void ProcessInterlacedPaletteScanline<TPixel>(
in PngHeader header,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> 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<TPixel>(
Configuration configuration,
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
int bytesPerPixel,
@ -240,16 +250,17 @@ internal static class PngScanlineProcessor
Rgb24 rgb24Trans)
where TPixel : unmanaged, IPixel<TPixel>
{
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<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(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<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>(
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
uint pixelOffset,
@ -315,18 +337,19 @@ internal static class PngScanlineProcessor
Rgb24 rgb24Trans)
where TPixel : unmanaged, IPixel<TPixel>
{
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<TPixel>(
Configuration configuration,
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
int bytesPerPixel,
int bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel>
{
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<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>(
in PngHeader header,
int bitDepth,
FrameControl frameControl,
ReadOnlySpan<byte> scanlineSpan,
Span<TPixel> rowSpan,
uint pixelOffset,
@ -431,15 +466,16 @@ internal static class PngScanlineProcessor
int bytesPerSample)
where TPixel : unmanaged, IPixel<TPixel>
{
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));

Loading…
Cancel
Save