Skip to content

Commit b7f3bbb

Browse files
Merge branch 'main' into dependabot/github_actions/codecov/codecov-action-6
2 parents 719ebb8 + 66f21f7 commit b7f3bbb

110 files changed

Lines changed: 1454 additions & 714 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/ImageSharp/Advanced/AotCompilerTools.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ internal static class AotCompilerTools
5454
/// <remarks>
5555
/// This method doesn't actually do anything but serves an important purpose...
5656
/// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception:
57-
/// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode."
57+
/// "Attempting to JIT compile method... HexadecatreeQuantizer.ConstructPalette... while running in aot-only mode."
5858
/// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT
5959
/// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on
6060
/// iOS so it bombs out.
@@ -479,7 +479,7 @@ private static void AotCompileResampler<TPixel, TResampler>()
479479
private static void AotCompileQuantizers<TPixel>()
480480
where TPixel : unmanaged, IPixel<TPixel>
481481
{
482-
AotCompileQuantizer<TPixel, OctreeQuantizer>();
482+
AotCompileQuantizer<TPixel, HexadecatreeQuantizer>();
483483
AotCompileQuantizer<TPixel, PaletteQuantizer>();
484484
AotCompileQuantizer<TPixel, WebSafePaletteQuantizer>();
485485
AotCompileQuantizer<TPixel, WernerPaletteQuantizer>();
@@ -523,10 +523,8 @@ private static void AotCompilePixelSamplingStrategys<TPixel>()
523523
private static void AotCompilePixelMaps<TPixel>()
524524
where TPixel : unmanaged, IPixel<TPixel>
525525
{
526-
default(EuclideanPixelMap<TPixel, HybridCache>).GetClosestColor(default, out _);
527526
default(EuclideanPixelMap<TPixel, AccurateCache>).GetClosestColor(default, out _);
528527
default(EuclideanPixelMap<TPixel, CoarseCache>).GetClosestColor(default, out _);
529-
default(EuclideanPixelMap<TPixel, NullCache>).GetClosestColor(default, out _);
530528
}
531529

532530
/// <summary>
@@ -551,8 +549,8 @@ private static void AotCompileDither<TPixel, TDither>()
551549
where TPixel : unmanaged, IPixel<TPixel>
552550
where TDither : struct, IDither
553551
{
554-
OctreeQuantizer<TPixel> octree = default;
555-
default(TDither).ApplyQuantizationDither<OctreeQuantizer<TPixel>, TPixel>(ref octree, default, default, default);
552+
HexadecatreeQuantizer<TPixel> hexadecatree = default;
553+
default(TDither).ApplyQuantizationDither<HexadecatreeQuantizer<TPixel>, TPixel>(ref hexadecatree, default, default, default);
556554

557555
PaletteQuantizer<TPixel> palette = default;
558556
default(TDither).ApplyQuantizationDither<PaletteQuantizer<TPixel>, TPixel>(ref palette, default, default, default);

src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ public interface IRowOperation<TBuffer>
1515
/// </summary>
1616
/// <param name="bounds">The bounds of the operation.</param>
1717
/// <returns>The required buffer length.</returns>
18-
int GetRequiredBufferLength(Rectangle bounds);
18+
public int GetRequiredBufferLength(Rectangle bounds);
1919

2020
/// <summary>
2121
/// Invokes the method passing the row and a buffer.
2222
/// </summary>
2323
/// <param name="y">The row y coordinate.</param>
2424
/// <param name="span">The contiguous region of memory.</param>
25-
void Invoke(int y, Span<TBuffer> span);
25+
public void Invoke(int y, Span<TBuffer> span);
2626
}

src/ImageSharp/Advanced/ParallelRowIterator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static void IterateRows<T>(
6868
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps };
6969
RowOperationWrapper<T> wrappingOperation = new(top, bottom, verticalStep, in operation);
7070

71-
Parallel.For(
71+
_ = Parallel.For(
7272
0,
7373
numOfSteps,
7474
parallelOptions,
@@ -138,7 +138,7 @@ public static void IterateRows<T, TBuffer>(
138138
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps };
139139
RowOperationWrapper<T, TBuffer> wrappingOperation = new(top, bottom, verticalStep, bufferLength, allocator, in operation);
140140

141-
Parallel.For(
141+
_ = Parallel.For(
142142
0,
143143
numOfSteps,
144144
parallelOptions,
@@ -195,7 +195,7 @@ public static void IterateRowIntervals<T>(
195195
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps };
196196
RowIntervalOperationWrapper<T> wrappingOperation = new(top, bottom, verticalStep, in operation);
197197

198-
Parallel.For(
198+
_ = Parallel.For(
199199
0,
200200
numOfSteps,
201201
parallelOptions,
@@ -262,7 +262,7 @@ public static void IterateRowIntervals<T, TBuffer>(
262262
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = numOfSteps };
263263
RowIntervalOperationWrapper<T, TBuffer> wrappingOperation = new(top, bottom, verticalStep, bufferLength, allocator, in operation);
264264

265-
Parallel.For(
265+
_ = Parallel.For(
266266
0,
267267
numOfSteps,
268268
parallelOptions,

src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ internal static void ByteToNormalizedFloatReduce(
752752
/// Implementation is based on MagicScaler code:
753753
/// https://github.com/saucecontrol/PhotoSauce/blob/b5811908041200488aa18fdfd17df5fc457415dc/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L80-L182
754754
/// </remarks>
755-
internal static unsafe void ByteToNormalizedFloat(
755+
internal static void ByteToNormalizedFloat(
756756
ReadOnlySpan<byte> source,
757757
Span<float> destination)
758758
{
@@ -1172,8 +1172,10 @@ internal static void UnpackToRgbPlanesAvx2Reduce(
11721172
Vector256<byte> rgb, rg, bx;
11731173
Vector256<float> r, g, b;
11741174

1175+
// Each iteration consumes 8 Rgb24 pixels (24 bytes) but starts with a 32-byte load,
1176+
// so we need 3 extra pixels of addressable slack beyond the vectorized chunk.
11751177
const int bytesPerRgbStride = 24;
1176-
nuint count = (uint)source.Length / 8;
1178+
nuint count = source.Length > 3 ? (uint)(source.Length - 3) / 8 : 0;
11771179
for (nuint i = 0; i < count; i++)
11781180
{
11791181
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (uint)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte();
@@ -1193,10 +1195,10 @@ internal static void UnpackToRgbPlanesAvx2Reduce(
11931195
}
11941196

11951197
int sliceCount = (int)(count * 8);
1196-
redChannel = redChannel.Slice(sliceCount);
1197-
greenChannel = greenChannel.Slice(sliceCount);
1198-
blueChannel = blueChannel.Slice(sliceCount);
1199-
source = source.Slice(sliceCount);
1198+
redChannel = redChannel[sliceCount..];
1199+
greenChannel = greenChannel[sliceCount..];
1200+
blueChannel = blueChannel[sliceCount..];
1201+
source = source[sliceCount..];
12001202
}
12011203
}
12021204
}

src/ImageSharp/Formats/Bmp/BmpEncoder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public sealed class BmpEncoder : QuantizingImageEncoder
1313
/// <summary>
1414
/// Initializes a new instance of the <see cref="BmpEncoder"/> class.
1515
/// </summary>
16-
public BmpEncoder() => this.Quantizer = KnownQuantizers.Octree;
16+
public BmpEncoder() => this.Quantizer = KnownQuantizers.Hexadecatree;
1717

1818
/// <summary>
1919
/// Gets the number of bits per pixel.

src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public BmpEncoderCore(BmpEncoder encoder, MemoryAllocator memoryAllocator)
116116
this.bitsPerPixel = encoder.BitsPerPixel;
117117

118118
// TODO: Use a palette quantizer if supplied.
119-
this.quantizer = encoder.Quantizer ?? KnownQuantizers.Octree;
119+
this.quantizer = encoder.Quantizer ?? KnownQuantizers.Hexadecatree;
120120
this.pixelSamplingStrategy = encoder.PixelSamplingStrategy;
121121
this.transparentColorMode = encoder.TransparentColorMode;
122122
this.infoHeaderType = encoder.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3;

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ private bool ReadFrame<TPixel>(
468468
int length = this.currentLocalColorTableSize = this.imageDescriptor.LocalColorTableSize * 3;
469469
this.currentLocalColorTable ??= this.configuration.MemoryAllocator.Allocate<byte>(768, AllocationOptions.Clean);
470470
stream.Read(this.currentLocalColorTable.GetSpan()[..length]);
471-
rawColorTable = this.currentLocalColorTable!.GetSpan()[..length];
471+
rawColorTable = this.currentLocalColorTable.GetSpan()[..length];
472472
}
473473
else if (this.globalColorTable != null)
474474
{

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
117117

118118
if (globalQuantizer is null)
119119
{
120-
// Is this a gif with color information. If so use that, otherwise use octree.
120+
// Is this a gif with color information. If so use that, otherwise use the adaptive hexadecatree quantizer.
121121
if (gifMetadata.ColorTableMode == FrameColorTableMode.Global && gifMetadata.GlobalColorTable?.Length > 0)
122122
{
123123
int ti = GetTransparentIndex(quantized, frameMetadata);
@@ -132,12 +132,12 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
132132
}
133133
else
134134
{
135-
globalQuantizer = new OctreeQuantizer(options);
135+
globalQuantizer = new HexadecatreeQuantizer(options);
136136
}
137137
}
138138
else
139139
{
140-
globalQuantizer = new OctreeQuantizer(options);
140+
globalQuantizer = new HexadecatreeQuantizer(options);
141141
}
142142
}
143143

src/ImageSharp/Formats/Jpeg/Components/Encoder/SpectralConverter{TPixel}.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ private void ConvertStride(int spectralStep)
114114
Span<TPixel> sourceRow = this.pixelBuffer.DangerousGetRowSpan(srcIndex);
115115
PixelOperations<TPixel>.Instance.UnpackIntoRgbPlanes(rLane, gLane, bLane, sourceRow);
116116

117-
rLane.Slice(paddingStartIndex).Fill(rLane[paddingStartIndex - 1]);
118-
gLane.Slice(paddingStartIndex).Fill(gLane[paddingStartIndex - 1]);
119-
bLane.Slice(paddingStartIndex).Fill(bLane[paddingStartIndex - 1]);
117+
rLane.Slice(paddingStartIndex, paddedPixelsCount).Fill(rLane[paddingStartIndex - 1]);
118+
gLane.Slice(paddingStartIndex, paddedPixelsCount).Fill(gLane[paddingStartIndex - 1]);
119+
bLane.Slice(paddingStartIndex, paddedPixelsCount).Fill(bLane[paddingStartIndex - 1]);
120120

121121
// Convert from rgb24 to target pixel type
122122
JpegColorConverterBase.ComponentValues values = new(this.componentProcessors, y);

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
214214
break;
215215
case PngChunkType.FrameData:
216216
{
217-
if (frameCount >= this.maxFrames)
217+
if (frameCount > this.maxFrames)
218218
{
219219
goto EOF;
220220
}
@@ -275,7 +275,7 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
275275
previousFrameControl = currentFrameControl;
276276
}
277277

278-
if (frameCount >= this.maxFrames)
278+
if (frameCount > this.maxFrames)
279279
{
280280
goto EOF;
281281
}
@@ -402,7 +402,7 @@ protected override ImageInfo Identify(BufferedReadStream stream, CancellationTok
402402
break;
403403
case PngChunkType.FrameControl:
404404
++frameCount;
405-
if (frameCount >= this.maxFrames)
405+
if (frameCount > this.maxFrames)
406406
{
407407
break;
408408
}
@@ -411,8 +411,12 @@ protected override ImageInfo Identify(BufferedReadStream stream, CancellationTok
411411

412412
break;
413413
case PngChunkType.FrameData:
414-
if (frameCount >= this.maxFrames)
414+
if (frameCount > this.maxFrames)
415415
{
416+
// Must skip the chunk data even when we've hit maxFrames, because TryReadChunk
417+
// restores the stream position to the start of the fdAT data after CRC validation.
418+
this.SkipChunkDataAndCrc(chunk);
419+
this.SkipRemainingFrameDataChunks(buffer);
416420
break;
417421
}
418422

@@ -428,9 +432,10 @@ protected override ImageInfo Identify(BufferedReadStream stream, CancellationTok
428432

429433
InitializeFrameMetadata(framesMetadata, currentFrameControl.Value);
430434

431-
// Skip sequence number
432-
this.currentStream.Skip(4);
435+
// Skip data for this and all remaining FrameData chunks belonging to the same frame
436+
// (comparable to how Decode consumes them via ReadScanlines + ReadNextFrameDataChunk).
433437
this.SkipChunkDataAndCrc(chunk);
438+
this.SkipRemainingFrameDataChunks(buffer);
434439
break;
435440
case PngChunkType.Data:
436441

@@ -2093,6 +2098,31 @@ private int ReadNextFrameDataChunk()
20932098
return 0;
20942099
}
20952100

2101+
/// <summary>
2102+
/// Skips any remaining <see cref="PngChunkType.FrameData"/> chunks belonging to the current frame.
2103+
/// This mirrors how <see cref="ReadNextFrameDataChunk"/> is used during decoding:
2104+
/// consecutive fdAT chunks are consumed until a non-fdAT chunk is encountered,
2105+
/// which is stored in <see cref="nextChunk"/> for the next iteration.
2106+
/// </summary>
2107+
/// <param name="buffer">Temporary buffer.</param>
2108+
private void SkipRemainingFrameDataChunks(Span<byte> buffer)
2109+
{
2110+
while (this.TryReadChunk(buffer, out PngChunk chunk))
2111+
{
2112+
if (chunk.Type is PngChunkType.FrameData)
2113+
{
2114+
chunk.Data?.Dispose();
2115+
this.SkipChunkDataAndCrc(chunk);
2116+
}
2117+
else
2118+
{
2119+
// Not a FrameData chunk; store it so the next TryReadChunk call returns it.
2120+
this.nextChunk = chunk;
2121+
return;
2122+
}
2123+
}
2124+
}
2125+
20962126
/// <summary>
20972127
/// Reads a chunk from the stream.
20982128
/// </summary>

0 commit comments

Comments
 (0)