Skip to content

Commit c253f39

Browse files
committed
Fix offset
1 parent 6e54822 commit c253f39

4 files changed

Lines changed: 174 additions & 118 deletions

File tree

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ public FrameControl(
5656
/// </summary>
5757
public int YOffset { get; }
5858

59+
/// <summary>
60+
/// Gets the X limit at which to render the following frame
61+
/// </summary>
62+
public uint XLimit => (uint)(this.XOffset + this.Width);
63+
64+
/// <summary>
65+
/// Gets the Y limit at which to render the following frame
66+
/// </summary>
67+
public uint YLimit => (uint)(this.YOffset + this.Height);
68+
5969
/// <summary>
6070
/// Gets the frame delay fraction numerator
6171
/// </summary>
@@ -104,14 +114,14 @@ public void Validate(PngHeader hdr)
104114
PngThrowHelper.ThrowInvalidParameter(this.Height, "Expected > 0");
105115
}
106116

107-
if (this.XOffset + this.Width > hdr.Width)
117+
if (this.XLimit > hdr.Width)
108118
{
109-
PngThrowHelper.ThrowInvalidParameter(this.XOffset, this.Width, $"The sum > {nameof(PngHeader)}.{nameof(PngHeader.Width)}");
119+
PngThrowHelper.ThrowInvalidParameter(this.XOffset, this.Width, $"The sum of them > {nameof(PngHeader)}.{nameof(PngHeader.Width)}");
110120
}
111121

112-
if (this.YOffset + this.Height > hdr.Height)
122+
if (this.YLimit > hdr.Height)
113123
{
114-
PngThrowHelper.ThrowInvalidParameter(this.YOffset, this.Height, "The sum > PngHeader.Height");
124+
PngThrowHelper.ThrowInvalidParameter(this.YOffset, this.Height, $"The sum of them > {nameof(PngHeader)}.{nameof(PngHeader.Height)}");
115125
}
116126
}
117127

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
228228
this.currentStream.Position += 4; // Skip sequence number
229229
return length - 4;
230230
},
231+
lastFrameControl.Value,
231232
cancellationToken);
232233
lastFrameControl = null;
233234
break;
@@ -237,7 +238,9 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
237238
this.InitializeImage(metadata, lastFrameControl, out image);
238239
}
239240

240-
this.ReadScanlines(chunk.Length, image.Frames.RootFrame, pngMetadata, this.ReadNextDataChunk, cancellationToken);
241+
FrameControl frameControl = lastFrameControl ?? new(0, this.header.Width, this.header.Height, 0, 0, 0, 0, default, default);
242+
243+
this.ReadScanlines(chunk.Length, image.Frames.RootFrame, pngMetadata, this.ReadNextDataChunk, frameControl, cancellationToken);
241244
lastFrameControl = null;
242245
break;
243246
case PngChunkType.Palette:
@@ -682,8 +685,9 @@ private int CalculateScanlineLength(int width)
682685
/// <param name="image"> The pixel data.</param>
683686
/// <param name="pngMetadata">The png metadata</param>
684687
/// <param name="getData">A delegate to get more data from the inner stream for <see cref="ZlibInflateStream"/>.</param>
688+
/// <param name="frameControl">The frame control</param>
685689
/// <param name="cancellationToken">The cancellation token.</param>
686-
private void ReadScanlines<TPixel>(int chunkLength, ImageFrame<TPixel> image, PngMetadata pngMetadata, Func<int> getData, CancellationToken cancellationToken)
690+
private void ReadScanlines<TPixel>(int chunkLength, ImageFrame<TPixel> image, PngMetadata pngMetadata, Func<int> getData, FrameControl frameControl, CancellationToken cancellationToken)
687691
where TPixel : unmanaged, IPixel<TPixel>
688692
{
689693
using ZlibInflateStream deframeStream = new(this.currentStream, getData);
@@ -692,28 +696,29 @@ private void ReadScanlines<TPixel>(int chunkLength, ImageFrame<TPixel> image, Pn
692696

693697
if (this.header.InterlaceMethod is PngInterlaceMode.Adam7)
694698
{
695-
this.DecodeInterlacedPixelData(dataStream, image, pngMetadata, cancellationToken);
699+
this.DecodeInterlacedPixelData(frameControl, dataStream, image, pngMetadata, cancellationToken);
696700
}
697701
else
698702
{
699-
this.DecodePixelData(dataStream, image, pngMetadata, cancellationToken);
703+
this.DecodePixelData(frameControl, dataStream, image, pngMetadata, cancellationToken);
700704
}
701705
}
702706

703707
/// <summary>
704708
/// Decodes the raw pixel data row by row
705709
/// </summary>
706710
/// <typeparam name="TPixel">The pixel format.</typeparam>
711+
/// <param name="frameControl">The frame control</param>
707712
/// <param name="compressedStream">The compressed pixel data stream.</param>
708713
/// <param name="image">The image to decode to.</param>
709714
/// <param name="pngMetadata">The png metadata</param>
710715
/// <param name="cancellationToken">The CancellationToken</param>
711-
private void DecodePixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
716+
private void DecodePixelData<TPixel>(FrameControl frameControl, DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
712717
where TPixel : unmanaged, IPixel<TPixel>
713718
{
714-
int currentRow = Adam7.FirstRow[0];
719+
int currentRow = frameControl.YOffset;
715720
int currentRowBytesRead = 0;
716-
int height = image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata) ? frameMetadata.Height : this.header.Height;
721+
int height = frameControl.Height;
717722
while (currentRow < height)
718723
{
719724
cancellationToken.ThrowIfCancellationRequested();
@@ -757,7 +762,7 @@ private void DecodePixelData<TPixel>(DeflateStream compressedStream, ImageFrame<
757762
break;
758763
}
759764

760-
this.ProcessDefilteredScanline(currentRow, scanlineSpan, image, pngMetadata);
765+
this.ProcessDefilteredScanline(frameControl, currentRow, scanlineSpan, image, pngMetadata);
761766

762767
this.SwapScanlineBuffers();
763768
currentRow++;
@@ -769,23 +774,19 @@ private void DecodePixelData<TPixel>(DeflateStream compressedStream, ImageFrame<
769774
/// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/>
770775
/// </summary>
771776
/// <typeparam name="TPixel">The pixel format.</typeparam>
777+
/// <param name="frameControl">The frame control</param>
772778
/// <param name="compressedStream">The compressed pixel data stream.</param>
773779
/// <param name="image">The current image.</param>
774780
/// <param name="pngMetadata">The png metadata.</param>
775781
/// <param name="cancellationToken">The cancellation token.</param>
776-
private void DecodeInterlacedPixelData<TPixel>(DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
782+
private void DecodeInterlacedPixelData<TPixel>(FrameControl frameControl, DeflateStream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata, CancellationToken cancellationToken)
777783
where TPixel : unmanaged, IPixel<TPixel>
778784
{
779-
int currentRow = Adam7.FirstRow[0];
785+
int currentRow = Adam7.FirstRow[0] + frameControl.YOffset;
780786
int currentRowBytesRead = 0;
781787
int pass = 0;
782-
int width = this.header.Width;
783-
int height = this.header.Height;
784-
if (image.Metadata.TryGetPngFrameMetadata(out PngFrameMetadata? frameMetadata))
785-
{
786-
width = frameMetadata.Width;
787-
height = frameMetadata.Height;
788-
}
788+
int width = frameControl.Width;
789+
int height = frameControl.Height;
789790

790791
Buffer2D<TPixel> imageBuffer = image.PixelBuffer;
791792
while (true)
@@ -848,7 +849,7 @@ private void DecodeInterlacedPixelData<TPixel>(DeflateStream compressedStream, I
848849
}
849850

850851
Span<TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(currentRow);
851-
this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[pass], Adam7.ColumnIncrement[pass]);
852+
this.ProcessInterlacedDefilteredScanline(frameControl, this.scanline.GetSpan(), rowSpan, pngMetadata, pixelOffset: Adam7.FirstColumn[pass], increment: Adam7.ColumnIncrement[pass]);
852853

853854
this.SwapScanlineBuffers();
854855

@@ -874,11 +875,12 @@ private void DecodeInterlacedPixelData<TPixel>(DeflateStream compressedStream, I
874875
/// Processes the de-filtered scanline filling the image pixel data
875876
/// </summary>
876877
/// <typeparam name="TPixel">The pixel format.</typeparam>
878+
/// <param name="frameControl">The frame control</param>
877879
/// <param name="currentRow">The index of the current scanline being processed.</param>
878880
/// <param name="defilteredScanline">The de-filtered scanline</param>
879881
/// <param name="pixels">The image</param>
880882
/// <param name="pngMetadata">The png metadata.</param>
881-
private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetadata pngMetadata)
883+
private void ProcessDefilteredScanline<TPixel>(FrameControl frameControl, int currentRow, ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetadata pngMetadata)
882884
where TPixel : unmanaged, IPixel<TPixel>
883885
{
884886
Span<TPixel> rowSpan = pixels.PixelBuffer.DangerousGetRowSpan(currentRow);
@@ -902,7 +904,8 @@ private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte
902904
{
903905
case PngColorType.Grayscale:
904906
PngScanlineProcessor.ProcessGrayscaleScanline(
905-
this.header,
907+
this.header.BitDepth,
908+
frameControl,
906909
scanlineSpan,
907910
rowSpan,
908911
pngMetadata.HasTransparency,
@@ -913,7 +916,8 @@ private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte
913916

914917
case PngColorType.GrayscaleWithAlpha:
915918
PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline(
916-
this.header,
919+
this.header.BitDepth,
920+
frameControl,
917921
scanlineSpan,
918922
rowSpan,
919923
(uint)this.bytesPerPixel,
@@ -923,7 +927,7 @@ private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte
923927

924928
case PngColorType.Palette:
925929
PngScanlineProcessor.ProcessPaletteScanline(
926-
this.header,
930+
frameControl,
927931
scanlineSpan,
928932
rowSpan,
929933
this.palette,
@@ -933,8 +937,8 @@ private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte
933937

934938
case PngColorType.Rgb:
935939
PngScanlineProcessor.ProcessRgbScanline(
936-
this.configuration,
937-
this.header,
940+
this.header.BitDepth,
941+
frameControl,
938942
scanlineSpan,
939943
rowSpan,
940944
this.bytesPerPixel,
@@ -947,8 +951,8 @@ private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte
947951

948952
case PngColorType.RgbWithAlpha:
949953
PngScanlineProcessor.ProcessRgbaScanline(
950-
this.configuration,
951-
this.header,
954+
this.header.BitDepth,
955+
frameControl,
952956
scanlineSpan,
953957
rowSpan,
954958
this.bytesPerPixel,
@@ -967,12 +971,13 @@ private void ProcessDefilteredScanline<TPixel>(int currentRow, ReadOnlySpan<byte
967971
/// Processes the interlaced de-filtered scanline filling the image pixel data
968972
/// </summary>
969973
/// <typeparam name="TPixel">The pixel format.</typeparam>
974+
/// <param name="frameControl">The frame control</param>
970975
/// <param name="defilteredScanline">The de-filtered scanline</param>
971976
/// <param name="rowSpan">The current image row.</param>
972977
/// <param name="pngMetadata">The png metadata.</param>
973978
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
974979
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
975-
private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1)
980+
private void ProcessInterlacedDefilteredScanline<TPixel>(FrameControl frameControl, ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1)
976981
where TPixel : unmanaged, IPixel<TPixel>
977982
{
978983
// Trim the first marker byte from the buffer
@@ -994,7 +999,8 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
994999
{
9951000
case PngColorType.Grayscale:
9961001
PngScanlineProcessor.ProcessInterlacedGrayscaleScanline(
997-
this.header,
1002+
this.header.BitDepth,
1003+
frameControl,
9981004
scanlineSpan,
9991005
rowSpan,
10001006
(uint)pixelOffset,
@@ -1007,7 +1013,8 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
10071013

10081014
case PngColorType.GrayscaleWithAlpha:
10091015
PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline(
1010-
this.header,
1016+
this.header.BitDepth,
1017+
frameControl,
10111018
scanlineSpan,
10121019
rowSpan,
10131020
(uint)pixelOffset,
@@ -1019,7 +1026,7 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
10191026

10201027
case PngColorType.Palette:
10211028
PngScanlineProcessor.ProcessInterlacedPaletteScanline(
1022-
this.header,
1029+
frameControl,
10231030
scanlineSpan,
10241031
rowSpan,
10251032
(uint)pixelOffset,
@@ -1031,7 +1038,8 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
10311038

10321039
case PngColorType.Rgb:
10331040
PngScanlineProcessor.ProcessInterlacedRgbScanline(
1034-
this.header,
1041+
this.header.BitDepth,
1042+
frameControl,
10351043
scanlineSpan,
10361044
rowSpan,
10371045
(uint)pixelOffset,
@@ -1046,7 +1054,8 @@ private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defi
10461054

10471055
case PngColorType.RgbWithAlpha:
10481056
PngScanlineProcessor.ProcessInterlacedRgbaScanline(
1049-
this.header,
1057+
this.header.BitDepth,
1058+
frameControl,
10501059
scanlineSpan,
10511060
rowSpan,
10521061
(uint)pixelOffset,

0 commit comments

Comments
 (0)