From 5443840e97b5f4c352cc0ccd65f722f269726669 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Thu, 30 Jan 2025 07:02:07 +0100 Subject: [PATCH 1/9] Install libgdiplus on ubuntu --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 435c629bc..5900a531b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -73,7 +73,7 @@ jobs: steps: - name: Install libgdi+, which is required for tests running on ubuntu - if: ${{ matrix.options.os == 'buildjet-4vcpu-ubuntu-2204-arm' }} + if: ${{ contains(matrix.options.os, 'ubuntu') }} run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev - name: Git Config From eff8eee77b3a979014d09ec5063518e16617be7a Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Thu, 30 Jan 2025 07:21:53 +0100 Subject: [PATCH 2/9] Add libgdi installation to coverage --- .github/workflows/code-coverage.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index cd22fe5e5..30cb9d0f5 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -17,6 +17,11 @@ jobs: runs-on: ${{matrix.options.os}} steps: + + - name: Install libgdi+, which is required for tests running on ubuntu + if: ${{ contains(matrix.options.os, 'ubuntu') }} + run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev + - name: Git Config shell: bash run: | From 261fab1dac7ad905ee4c4a079f48dcf3894ccc58 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 4 Feb 2025 14:44:00 +1000 Subject: [PATCH 3/9] Replicate #2874 --- src/ImageSharp/Formats/Png/PngThrowHelper.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 31 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngThrowHelper.cs b/src/ImageSharp/Formats/Png/PngThrowHelper.cs index 0552e9a79..8dc70e1d9 100644 --- a/src/ImageSharp/Formats/Png/PngThrowHelper.cs +++ b/src/ImageSharp/Formats/Png/PngThrowHelper.cs @@ -44,7 +44,7 @@ internal static class PngThrowHelper => throw new NotSupportedException($"Invalid {name}. {message}. Was '{value}'."); [DoesNotReturn] - public static void ThrowInvalidParameter(object value1, object value2, string message, [CallerArgumentExpression(nameof(value1))] string name1 = "", [CallerArgumentExpression(nameof(value1))] string name2 = "") + public static void ThrowInvalidParameter(object value1, object value2, string message, [CallerArgumentExpression(nameof(value1))] string name1 = "", [CallerArgumentExpression(nameof(value2))] string name2 = "") => throw new NotSupportedException($"Invalid {name1} or {name2}. {message}. Was '{value1}' and '{value2}'."); [DoesNotReturn] diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index d699a7b63..733692460 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -648,7 +648,7 @@ internal class TiffDecoderCore : ImageDecoderCore } /// - /// Decodes the image data for Tiff's which arrange the pixel data in tiles and the chunky configuration. + /// Decodes the image data for TIFFs which arrange the pixel data in tiles and the chunky configuration. /// /// The pixel format. /// The image frame to decode into. @@ -674,14 +674,10 @@ internal class TiffDecoderCore : ImageDecoderCore int width = pixels.Width; int height = pixels.Height; int bitsPerPixel = this.BitsPerPixel; - - int bytesPerRow = RoundUpToMultipleOfEight(width * bitsPerPixel); int bytesPerTileRow = RoundUpToMultipleOfEight(tileWidth * bitsPerPixel); - int uncompressedTilesSize = bytesPerTileRow * tileLength; - using IMemoryOwner tileBuffer = this.memoryAllocator.Allocate(uncompressedTilesSize, AllocationOptions.Clean); - using IMemoryOwner uncompressedPixelBuffer = this.memoryAllocator.Allocate(tilesDown * tileLength * bytesPerRow, AllocationOptions.Clean); + + using IMemoryOwner tileBuffer = this.memoryAllocator.Allocate(bytesPerTileRow * tileLength, AllocationOptions.Clean); Span tileBufferSpan = tileBuffer.GetSpan(); - Span uncompressedPixelBufferSpan = uncompressedPixelBuffer.GetSpan(); using TiffBaseDecompressor decompressor = this.CreateDecompressor(frame.Width, bitsPerPixel); TiffBaseColorDecoder colorDecoder = this.CreateChunkyColorDecoder(); @@ -689,13 +685,15 @@ internal class TiffDecoderCore : ImageDecoderCore int tileIndex = 0; for (int tileY = 0; tileY < tilesDown; tileY++) { - int remainingPixelsInRow = width; + int rowStartY = tileY * tileLength; + int rowEndY = Math.Min(rowStartY + tileLength, height); + for (int tileX = 0; tileX < tilesAcross; tileX++) { cancellationToken.ThrowIfCancellationRequested(); - int uncompressedPixelBufferOffset = tileY * tileLength * bytesPerRow; bool isLastHorizontalTile = tileX == tilesAcross - 1; + int remainingPixelsInRow = width - (tileX * tileWidth); decompressor.Decompress( this.inputStream, @@ -706,22 +704,21 @@ internal class TiffDecoderCore : ImageDecoderCore cancellationToken); int tileBufferOffset = 0; - uncompressedPixelBufferOffset += bytesPerTileRow * tileX; int bytesToCopy = isLastHorizontalTile ? RoundUpToMultipleOfEight(bitsPerPixel * remainingPixelsInRow) : bytesPerTileRow; - for (int y = 0; y < tileLength; y++) + int rowWidth = Math.Min(tileWidth, remainingPixelsInRow); + int left = tileX * tileWidth; + + for (int y = rowStartY; y < rowEndY; y++) { - Span uncompressedPixelRow = uncompressedPixelBufferSpan.Slice(uncompressedPixelBufferOffset, bytesToCopy); - tileBufferSpan.Slice(tileBufferOffset, bytesToCopy).CopyTo(uncompressedPixelRow); + // Decode the tile row directly into the pixel buffer. + ReadOnlySpan tileRowSpan = tileBufferSpan.Slice(tileBufferOffset, bytesToCopy); + colorDecoder.Decode(tileRowSpan, pixels, left, y, rowWidth, 1); tileBufferOffset += bytesPerTileRow; - uncompressedPixelBufferOffset += bytesPerRow; } - remainingPixelsInRow -= tileWidth; tileIndex++; } } - - colorDecoder.Decode(uncompressedPixelBufferSpan, pixels, 0, 0, width, height); } private TiffBaseColorDecoder CreateChunkyColorDecoder() From 9be7d8440047d9407cace008476adc3fe870e8d9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 10 Feb 2025 14:24:55 +1000 Subject: [PATCH 4/9] Get the benchmarks working again. --- .../Bulk/FromRgba32Bytes.cs | 4 +- .../Bulk/Vector4Factory.cs | 2 +- .../Codecs/Bmp/DecodeBmp.cs | 17 +++--- .../Codecs/Bmp/EncodeBmp.cs | 8 ++- .../Codecs/Bmp/EncodeBmpMultiple.cs | 2 + .../Codecs/Gif/DecodeGif.cs | 12 ++-- .../Codecs/Gif/EncodeGif.cs | 10 ++-- .../Codecs/Gif/EncodeGifMultiple.cs | 4 +- .../Block8x8F_LoadFromInt16.cs | 13 +--- .../BlockOperations/Block8x8F_Quantize.cs | 2 +- .../Jpeg/BlockOperations/Block8x8F_Round.cs | 60 +++++++++---------- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 19 +++--- .../Codecs/Jpeg/DecodeJpeg_Aggregate.cs | 10 ++-- .../Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs | 17 +++--- .../Codecs/Jpeg/EncodeJpegComparison.cs | 9 ++- .../Codecs/Jpeg/EncodeJpegFeatures.cs | 10 ++-- .../Codecs/MultiImageBenchmarkBase.cs | 28 +++++---- .../Codecs/Png/DecodePng.cs | 2 + .../Codecs/Png/EncodeIndexedPng.cs | 26 ++++---- .../Codecs/Png/EncodePng.cs | 10 ++-- .../Codecs/Tga/DecodeTga.cs | 2 +- .../Codecs/Tga/EncodeTga.cs | 4 +- .../Codecs/Tiff/DecodeTiff.cs | 2 + .../Codecs/Tiff/EncodeTiff.cs | 46 +++++--------- .../Color/ColorEquality.cs | 2 + .../General/GetSetPixel.cs | 2 + .../General/IO/BufferedStreams.cs | 8 +-- .../PixelConversion_ConvertFromRgba32.cs | 18 +++--- .../General/StructCasting.cs | 5 +- .../General/Vectorization/Divide.cs | 24 ++++---- .../General/Vectorization/Multiply.cs | 18 +++--- .../Vectorization/SIMDBenchmarkBase.cs | 44 +++++++------- .../ImageSharp.Benchmarks.csproj | 20 ++++++- .../LoadResizeSaveStressRunner.cs | 4 +- .../ImageSharp.Benchmarks/Processing/Crop.cs | 10 ++-- .../Processing/Resize.cs | 36 ++++++----- 36 files changed, 262 insertions(+), 248 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs index 6cae20853..92d5bcdbf 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/FromRgba32Bytes.cs @@ -62,9 +62,7 @@ public abstract class FromRgba32Bytes => PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); } -public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes -{ -} +public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes; public class FromRgba32Bytes_ToBgra32 : FromRgba32Bytes { diff --git a/tests/ImageSharp.Benchmarks/Bulk/Vector4Factory.cs b/tests/ImageSharp.Benchmarks/Bulk/Vector4Factory.cs index aa555f5c4..0842a6845 100644 --- a/tests/ImageSharp.Benchmarks/Bulk/Vector4Factory.cs +++ b/tests/ImageSharp.Benchmarks/Bulk/Vector4Factory.cs @@ -30,5 +30,5 @@ internal static class Vector4Factory } private static float GetRandomFloat(Random rnd, float minVal, float maxVal) - => (float)rnd.NextDouble() * (maxVal - minVal) + minVal; + => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs index eb5b3c49d..d56cffd35 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -19,12 +20,7 @@ public class DecodeBmp [GlobalSetup] public void ReadImages() - { - if (this.bmpBytes == null) - { - this.bmpBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } + => this.bmpBytes ??= File.ReadAllBytes(this.TestImageFullPath); [Params(TestImages.Bmp.Car)] public string TestImage { get; set; } @@ -32,16 +28,17 @@ public class DecodeBmp [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] public SDSize BmpSystemDrawing() { - using var memoryStream = new MemoryStream(this.bmpBytes); - using var image = SDImage.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.bmpBytes); + using SDImage image = SDImage.FromStream(memoryStream); return image.Size; } [Benchmark(Description = "ImageSharp Bmp")] public Size BmpImageSharp() { - using var memoryStream = new MemoryStream(this.bmpBytes); - using var image = Image.Load(memoryStream); + using MemoryStream memoryStream = new(this.bmpBytes); + using Image image = Image.Load(memoryStream); return new Size(image.Width, image.Height); } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs index 3e6cfa51b..e61f31499 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; @@ -12,7 +13,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [Config(typeof(Config.Short))] public class EncodeBmp { - private Stream bmpStream; + private FileStream bmpStream; private SDImage bmpDrawing; private Image bmpCore; @@ -40,14 +41,15 @@ public class EncodeBmp [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] public void BmpSystemDrawing() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpDrawing.Save(memoryStream, ImageFormat.Bmp); } [Benchmark(Description = "ImageSharp Bmp")] public void BmpImageSharp() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpCore.SaveAsBmp(memoryStream); } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs index fdef1b2bf..457abbf0e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Bmp; @@ -28,3 +29,4 @@ public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded return null; }); } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs index ef5db2f1e..8c255b61a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -26,22 +27,23 @@ public class DecodeGif } } - [Params(TestImages.Gif.Cheers)] + [Params(TestImages.Gif.Rings)] public string TestImage { get; set; } [Benchmark(Baseline = true, Description = "System.Drawing Gif")] public SDSize GifSystemDrawing() { - using var memoryStream = new MemoryStream(this.gifBytes); - using var image = SDImage.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.gifBytes); + using SDImage image = SDImage.FromStream(memoryStream); return image.Size; } [Benchmark(Description = "ImageSharp Gif")] public Size GifImageSharp() { - using var memoryStream = new MemoryStream(this.gifBytes); - using var image = Image.Load(memoryStream); + using MemoryStream memoryStream = new(this.gifBytes); + using Image image = Image.Load(memoryStream); return new Size(image.Width, image.Height); } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs index c3644221e..46ced0351 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Gif; @@ -16,12 +17,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; public class EncodeGif { // System.Drawing needs this. - private Stream bmpStream; + private FileStream bmpStream; private SDImage bmpDrawing; private Image bmpCore; // Try to get as close to System.Drawing's output as possible - private readonly GifEncoder encoder = new GifEncoder + private readonly GifEncoder encoder = new() { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) }; @@ -53,14 +54,15 @@ public class EncodeGif [Benchmark(Baseline = true, Description = "System.Drawing Gif")] public void GifSystemDrawing() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpDrawing.Save(memoryStream, ImageFormat.Gif); } [Benchmark(Description = "ImageSharp Gif")] public void GifImageSharp() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpCore.SaveAsGif(memoryStream, this.encoder); } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs index 9557f616c..7bd1eb414 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Gif; @@ -22,7 +23,7 @@ public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded => this.ForEachImageSharpImage((img, ms) => { // Try to get as close to System.Drawing's output as possible - var options = new GifEncoder + GifEncoder options = new() { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = KnownDitherings.Bayer4x4 }) }; @@ -39,3 +40,4 @@ public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded return null; }); } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs index 91255c946..7a8502c2c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs @@ -12,8 +12,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations; public class Block8x8F_LoadFromInt16 { private Block8x8 source; - - private Block8x8F dest = default; + private Block8x8F destination; [GlobalSetup] public void Setup() @@ -30,16 +29,10 @@ public class Block8x8F_LoadFromInt16 } [Benchmark(Baseline = true)] - public void Scalar() - { - this.dest.LoadFromInt16Scalar(ref this.source); - } + public void Scalar() => this.destination.LoadFromInt16Scalar(ref this.source); [Benchmark] - public void ExtendedAvx2() - { - this.dest.LoadFromInt16ExtendedAvx2(ref this.source); - } + public void ExtendedAvx2() => this.destination.LoadFromInt16ExtendedAvx2(ref this.source); // RESULT: // Method | Mean | Error | StdDev | Scaled | diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs index b1718759e..88b8877e5 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Quantize.cs @@ -11,7 +11,7 @@ public class Block8x8F_Quantize { private Block8x8F block = CreateFromScalar(1); private Block8x8F quant = CreateFromScalar(1); - private Block8x8 result = default; + private Block8x8 result; [Benchmark] public short Quantize() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index 8a520b22d..1d8385168 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -36,7 +36,7 @@ public unsafe class Block8x8F_Round if (ptr % 16 != 0) { - throw new Exception("ptr is unaligned"); + throw new InvalidOperationException("ptr is unaligned"); } this.alignedPtr = (float*)ptr; @@ -67,21 +67,21 @@ public unsafe class Block8x8F_Round ref Block8x8F b = ref this.block; ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = SimdUtils.FastRound(row0); + row0 = row0.FastRound(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = SimdUtils.FastRound(row1); + row1 = row1.FastRound(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = SimdUtils.FastRound(row2); + row2 = row2.FastRound(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = SimdUtils.FastRound(row3); + row3 = row3.FastRound(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = SimdUtils.FastRound(row4); + row4 = row4.FastRound(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = SimdUtils.FastRound(row5); + row5 = row5.FastRound(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = SimdUtils.FastRound(row6); + row6 = row6.FastRound(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = SimdUtils.FastRound(row7); + row7 = row7.FastRound(); } [Benchmark] @@ -90,21 +90,21 @@ public unsafe class Block8x8F_Round ref Block8x8F b = ref Unsafe.AsRef(this.alignedPtr); ref Vector row0 = ref Unsafe.As>(ref b.V0L); - row0 = SimdUtils.FastRound(row0); + row0 = row0.FastRound(); ref Vector row1 = ref Unsafe.As>(ref b.V1L); - row1 = SimdUtils.FastRound(row1); + row1 = row1.FastRound(); ref Vector row2 = ref Unsafe.As>(ref b.V2L); - row2 = SimdUtils.FastRound(row2); + row2 = row2.FastRound(); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row3 = SimdUtils.FastRound(row3); + row3 = row3.FastRound(); ref Vector row4 = ref Unsafe.As>(ref b.V4L); - row4 = SimdUtils.FastRound(row4); + row4 = row4.FastRound(); ref Vector row5 = ref Unsafe.As>(ref b.V5L); - row5 = SimdUtils.FastRound(row5); + row5 = row5.FastRound(); ref Vector row6 = ref Unsafe.As>(ref b.V6L); - row6 = SimdUtils.FastRound(row6); + row6 = row6.FastRound(); ref Vector row7 = ref Unsafe.As>(ref b.V7L); - row7 = SimdUtils.FastRound(row7); + row7 = row7.FastRound(); } [Benchmark] @@ -117,20 +117,20 @@ public unsafe class Block8x8F_Round ref Vector row2 = ref Unsafe.As>(ref b.V2L); ref Vector row3 = ref Unsafe.As>(ref b.V3L); - row0 = SimdUtils.FastRound(row0); - row1 = SimdUtils.FastRound(row1); - row2 = SimdUtils.FastRound(row2); - row3 = SimdUtils.FastRound(row3); + row0 = row0.FastRound(); + row1 = row1.FastRound(); + row2 = row2.FastRound(); + row3 = row3.FastRound(); row0 = ref Unsafe.As>(ref b.V4L); row1 = ref Unsafe.As>(ref b.V5L); row2 = ref Unsafe.As>(ref b.V6L); row3 = ref Unsafe.As>(ref b.V7L); - row0 = SimdUtils.FastRound(row0); - row1 = SimdUtils.FastRound(row1); - row2 = SimdUtils.FastRound(row2); - row3 = SimdUtils.FastRound(row3); + row0 = row0.FastRound(); + row1 = row1.FastRound(); + row2 = row2.FastRound(); + row3 = row3.FastRound(); } [Benchmark] @@ -174,7 +174,7 @@ public unsafe class Block8x8F_Round } [Benchmark] - public unsafe void Sse41_V2() + public void Sse41_V2() { ref Vector128 p = ref Unsafe.As>(ref this.block); p = Sse41.RoundToNearestInteger(p); @@ -214,7 +214,7 @@ public unsafe class Block8x8F_Round } [Benchmark] - public unsafe void Sse41_V3() + public void Sse41_V3() { ref Vector128 p = ref Unsafe.As>(ref this.block); p = Sse41.RoundToNearestInteger(p); @@ -228,7 +228,7 @@ public unsafe class Block8x8F_Round } [Benchmark] - public unsafe void Sse41_V4() + public void Sse41_V4() { ref Vector128 p = ref Unsafe.As>(ref this.block); nuint offset = (uint)sizeof(Vector128); @@ -271,7 +271,7 @@ public unsafe class Block8x8F_Round } [Benchmark] - public unsafe void Sse41_V5_Unaligned() + public void Sse41_V5_Unaligned() { float* p = this.alignedPtr + 1; @@ -356,7 +356,7 @@ public unsafe class Block8x8F_Round } [Benchmark] - public unsafe void Sse41_V5_Aligned() + public void Sse41_V5_Aligned() { float* p = this.alignedPtr; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index f5178390f..cf35ebbaa 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -27,20 +28,20 @@ public class DecodeJpegParseStreamOnly [Benchmark(Baseline = true, Description = "System.Drawing FULL")] public SDSize JpegSystemDrawing() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = System.Drawing.Image.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.jpegBytes); + using System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream); return image.Size; } [Benchmark(Description = "JpegDecoderCore.ParseStream")] public void ParseStream() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var bufferedStream = new BufferedReadStream(Configuration.Default, memoryStream); - var options = new JpegDecoderOptions() { GeneralOptions = new() { SkipMetadata = true } }; + using MemoryStream memoryStream = new(this.jpegBytes); + using BufferedReadStream bufferedStream = new(Configuration.Default, memoryStream); + JpegDecoderOptions options = new() { GeneralOptions = new() { SkipMetadata = true } }; - using var decoder = new JpegDecoderCore(options); - var spectralConverter = new NoopSpectralConverter(); + using JpegDecoderCore decoder = new(options); + NoopSpectralConverter spectralConverter = new(); decoder.ParseStream(bufferedStream, spectralConverter, cancellationToken: default); } @@ -48,7 +49,7 @@ public class DecodeJpegParseStreamOnly // Nor we need to allocate final pixel buffer // Note: this still introduces virtual method call overhead for baseline interleaved images // There's no way to eliminate it as spectral conversion is built into the scan decoding loop for memory footprint reduction - private class NoopSpectralConverter : SpectralConverter + private sealed class NoopSpectralConverter : SpectralConverter { public override void ConvertStrideBaseline() { @@ -65,7 +66,7 @@ public class DecodeJpegParseStreamOnly } } } - +#endif /* BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1083 (20H2/October2020Update) Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs index 389fec88b..76b6d27bd 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -17,23 +18,24 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg; public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase { protected override IEnumerable InputImageSubfoldersOrFiles - => new[] - { + => + [ TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, - }; + ]; [Params(InputImageCategory.AllImages)] public override InputImageCategory InputCategory { get; set; } [Benchmark] public void ImageSharp() - => this.ForEachStream(ms => Image.Load(ms)); + => this.ForEachStream(Image.Load); [Benchmark(Baseline = true)] public void SystemDrawing() => this.ForEachStream(SDImage.FromStream); } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index 08df2580d..cf3c8a434 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Tests; @@ -35,26 +36,21 @@ public class DecodeJpeg_ImageSpecific [GlobalSetup] public void ReadImages() - { - if (this.jpegBytes == null) - { - this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } + => this.jpegBytes ??= File.ReadAllBytes(this.TestImageFullPath); [Benchmark(Baseline = true)] public SDSize SystemDrawing() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = SDImage.FromStream(memoryStream); + using MemoryStream memoryStream = new(this.jpegBytes); + using SDImage image = SDImage.FromStream(memoryStream); return image.Size; } [Benchmark] public Size ImageSharp() { - using var memoryStream = new MemoryStream(this.jpegBytes); - using var image = Image.Load(new DecoderOptions() { SkipMetadata = true }, memoryStream); + using MemoryStream memoryStream = new(this.jpegBytes); + using Image image = Image.Load(new DecoderOptions() { SkipMetadata = true }, memoryStream); return new Size(image.Width, image.Height); } @@ -71,3 +67,4 @@ public class DecodeJpeg_ImageSpecific | 'Decode Jpeg - ImageSharp' | Jpg/i(...)e.jpg [43] | 276.490 ms | 195.5104 ms | 10.7166 ms | 0.71 | 0.01 | - | - | - | 36022368 B | */ } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs index deb3125b3..c7cecd1a5 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs @@ -19,11 +19,6 @@ public class EncodeJpegComparison { // Big enough, 4:4:4 chroma sampling private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; - - // Change/add parameters for extra benchmarks - [Params(75, 90, 100)] - public int Quality; - private MemoryStream destinationStream; // ImageSharp @@ -33,6 +28,10 @@ public class EncodeJpegComparison // SkiaSharp private SKBitmap imageSkiaSharp; + // Change/add parameters for extra benchmarks + [Params(75, 90, 100)] + public int Quality { get; set; } + [GlobalSetup(Target = nameof(BenchmarkImageSharp))] public void SetupImageSharp() { diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs index 0692c5a3b..858917995 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs @@ -20,19 +20,19 @@ public class EncodeJpegFeatures // No metadata private const string TestImage = TestImages.Jpeg.Baseline.Calliphora; - public static IEnumerable ColorSpaceValues => new[] - { + public static IEnumerable ColorSpaceValues => + [ JpegColorType.Luminance, JpegColorType.Rgb, JpegColorType.YCbCrRatio420, JpegColorType.YCbCrRatio444, - }; + ]; [Params(75, 90, 100)] - public int Quality; + public int Quality { get; set; } [ParamsSource(nameof(ColorSpaceValues), Priority = -100)] - public JpegColorType TargetColorSpace; + public JpegColorType TargetColorSpace { get; set; } private Image bmpCore; private JpegEncoder encoder; diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index b75d012f9..8bb0522ab 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing; using System.Numerics; using BenchmarkDotNet.Attributes; @@ -11,11 +12,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; public abstract class MultiImageBenchmarkBase { - protected Dictionary FileNamesToBytes { get; set; } = new Dictionary(); + protected Dictionary FileNamesToBytes { get; set; } = []; - protected Dictionary> FileNamesToImageSharpImages { get; set; } = new Dictionary>(); + protected Dictionary> FileNamesToImageSharpImages { get; set; } = []; - protected Dictionary FileNamesToSystemDrawingImages { get; set; } = new Dictionary(); + protected Dictionary FileNamesToSystemDrawingImages { get; set; } = []; /// /// The values of this enum separate input files into categories. @@ -43,12 +44,12 @@ public abstract class MultiImageBenchmarkBase protected virtual string BaseFolder => TestEnvironment.InputImagesDirectoryFullPath; - protected virtual IEnumerable SearchPatterns => new[] { "*.*" }; + protected virtual IEnumerable SearchPatterns => ["*.*"]; /// /// Gets the file names containing these strings are substrings are not processed by the benchmark. /// - protected virtual IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; + protected virtual IEnumerable ExcludeSubstringsInFileNames => ["badeof", "BadEof", "CriticalEOF"]; /// /// Gets folders containing files OR files to be processed by the benchmark. @@ -70,7 +71,7 @@ public abstract class MultiImageBenchmarkBase InputImageCategory.AllImages => input, InputImageCategory.SmallImagesOnly => input.Where(kv => checkIfSmall(kv.Value)), InputImageCategory.LargeImagesOnly => input.Where(kv => !checkIfSmall(kv.Value)), - _ => throw new ArgumentOutOfRangeException(), + _ => throw new ArgumentOutOfRangeException(nameof(input), "Invalid input category") }; protected IEnumerable> FileNames2Bytes @@ -86,7 +87,7 @@ public abstract class MultiImageBenchmarkBase { if (!Vector.IsHardwareAccelerated) { - throw new Exception("Vector.IsHardwareAccelerated == false! Check your build settings!"); + throw new InvalidOperationException("Vector.IsHardwareAccelerated == false! Check your build settings!"); } // Console.WriteLine("Vector.IsHardwareAccelerated: " + Vector.IsHardwareAccelerated); @@ -103,13 +104,13 @@ public abstract class MultiImageBenchmarkBase continue; } - string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray(); + string[] excludeStrings = this.ExcludeSubstringsInFileNames.ToArray(); string[] allFiles = this.SearchPatterns.SelectMany( f => Directory.EnumerateFiles(path, f, SearchOption.AllDirectories) - .Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray(); + .Where(fn => !excludeStrings.Any(excludeStr => fn.Contains(excludeStr, StringComparison.OrdinalIgnoreCase)))).ToArray(); foreach (string fn in allFiles) { @@ -126,7 +127,7 @@ public abstract class MultiImageBenchmarkBase { foreach (KeyValuePair kv in this.FileNames2Bytes) { - using var memoryStream = new MemoryStream(kv.Value); + using MemoryStream memoryStream = new(kv.Value); try { object obj = operation(memoryStream); @@ -150,7 +151,7 @@ public abstract class MultiImageBenchmarkBase byte[] bytes = kv.Value; string fn = kv.Key; - using (var ms1 = new MemoryStream(bytes)) + using (MemoryStream ms1 = new(bytes)) { this.FileNamesToImageSharpImages[fn] = Image.Load(ms1); } @@ -191,7 +192,7 @@ public abstract class MultiImageBenchmarkBase protected void ForEachImageSharpImage(Func, MemoryStream, object> operation) { - using var workStream = new MemoryStream(); + using MemoryStream workStream = new(); this.ForEachImageSharpImage( img => { @@ -222,7 +223,7 @@ public abstract class MultiImageBenchmarkBase protected void ForEachSystemDrawingImage(Func operation) { - using var workStream = new MemoryStream(); + using MemoryStream workStream = new(); this.ForEachSystemDrawingImage( img => { @@ -236,3 +237,4 @@ public abstract class MultiImageBenchmarkBase } } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs index 2cf62fccf..0a6b880b7 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -40,3 +41,4 @@ public class DecodePng return image.Size; } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs index a45e7aea9..125b42680 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodeIndexedPng.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; public class EncodeIndexedPng { // System.Drawing needs this. - private Stream bmpStream; + private FileStream bmpStream; private Image bmpCore; [GlobalSetup] @@ -43,48 +43,48 @@ public class EncodeIndexedPng [Benchmark(Baseline = true, Description = "ImageSharp Octree Png")] public void PngCoreOctree() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.Octree }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = KnownQuantizers.Octree }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Octree NoDither Png")] public void PngCoreOctreeNoDither() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }) }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = new OctreeQuantizer(new QuantizerOptions { Dither = null }) }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Palette Png")] public void PngCorePalette() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = KnownQuantizers.WebSafe }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Palette NoDither Png")] public void PngCorePaletteNoDither() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null }) }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = new WebSafePaletteQuantizer(new QuantizerOptions { Dither = null }) }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Wu Png")] public void PngCoreWu() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = KnownQuantizers.Wu }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = KnownQuantizers.Wu }; this.bmpCore.SaveAsPng(memoryStream, options); } [Benchmark(Description = "ImageSharp Wu NoDither Png")] public void PngCoreWuNoDither() { - using var memoryStream = new MemoryStream(); - var options = new PngEncoder { Quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }), ColorType = PngColorType.Palette }; + using MemoryStream memoryStream = new(); + PngEncoder options = new() { Quantizer = new WuQuantizer(new QuantizerOptions { Dither = null }), ColorType = PngColorType.Palette }; this.bmpCore.SaveAsPng(memoryStream, options); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs index 428791478..50935efcd 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Png; @@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; public class EncodePng { // System.Drawing needs this. - private Stream bmpStream; + private FileStream bmpStream; private SDImage bmpDrawing; private Image bmpCore; @@ -46,15 +47,16 @@ public class EncodePng [Benchmark(Baseline = true, Description = "System.Drawing Png")] public void PngSystemDrawing() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.bmpDrawing.Save(memoryStream, ImageFormat.Png); } [Benchmark(Description = "ImageSharp Png")] public void PngCore() { - using var memoryStream = new MemoryStream(); - var encoder = new PngEncoder { FilterMethod = PngFilterMethod.None }; + using MemoryStream memoryStream = new(); + PngEncoder encoder = new() { FilterMethod = PngFilterMethod.None }; this.bmpCore.SaveAsPng(memoryStream, encoder); } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs index 7a3fc0b39..a56b733f0 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs @@ -48,7 +48,7 @@ public class DecodeTga return image.Width; } - private class PfimAllocator : IImageAllocator + private sealed class PfimAllocator : IImageAllocator { private int rented; private readonly ArrayPool shared = ArrayPool.Shared; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs index a7f5e3589..169a44d91 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/EncodeTga.cs @@ -41,14 +41,14 @@ public class EncodeTga [Benchmark(Baseline = true, Description = "Magick Tga")] public void MagickTga() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.tgaMagick.Write(memoryStream, MagickFormat.Tga); } [Benchmark(Description = "ImageSharp Tga")] public void ImageSharpTga() { - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.tga.SaveAsTga(memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs index ecb87e16c..9beed63b9 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS // Enable this for using larger Tiff files. Those files are very large (> 700MB) and therefor not part of the git repo. // Use the scripts gen_big.ps1 and gen_medium.ps1 in tests\Images\Input\Tiff\Benchmarks to generate those images. //// #define BIG_TESTS @@ -82,3 +83,4 @@ public class DecodeTiff return image.Size; } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs index d3e394405..67c12773a 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [Config(typeof(Config.Short))] public class EncodeTiff { - private Stream stream; + private FileStream stream; private SDImage drawing; private Image core; @@ -60,12 +61,12 @@ public class EncodeTiff public void SystemDrawing() { ImageCodecInfo codec = FindCodecForType("image/tiff"); - using var parameters = new EncoderParameters(1) + using EncoderParameters parameters = new(1) { Param = { [0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)) } }; - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); this.drawing.Save(memoryStream, codec, parameters); } @@ -77,8 +78,8 @@ public class EncodeTiff TiffPhotometricInterpretation.WhiteIsZero : TiffPhotometricInterpretation.Rgb; - var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; - using var memoryStream = new MemoryStream(); + TiffEncoder encoder = new() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; + using MemoryStream memoryStream = new(); this.core.SaveAsTiff(memoryStream, encoder); } @@ -98,33 +99,16 @@ public class EncodeTiff } private static EncoderValue Cast(TiffCompression compression) - { - switch (compression) + => compression switch { - case TiffCompression.None: - return EncoderValue.CompressionNone; - - case TiffCompression.CcittGroup3Fax: - return EncoderValue.CompressionCCITT3; - - case TiffCompression.Ccitt1D: - return EncoderValue.CompressionRle; - - case TiffCompression.Lzw: - return EncoderValue.CompressionLZW; - - default: - throw new NotSupportedException(compression.ToString()); - } - } + TiffCompression.None => EncoderValue.CompressionNone, + TiffCompression.CcittGroup3Fax => EncoderValue.CompressionCCITT3, + TiffCompression.Ccitt1D => EncoderValue.CompressionRle, + TiffCompression.Lzw => EncoderValue.CompressionLZW, + _ => throw new NotSupportedException(compression.ToString()), + }; public static bool IsOneBitCompression(TiffCompression compression) - { - if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) - { - return true; - } - - return false; - } + => compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax; } +#endif diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index 5166c89a9..de6cc420e 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SystemColor = System.Drawing.Color; @@ -17,3 +18,4 @@ public class ColorEquality public bool ColorEqual() => new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); } +#endif diff --git a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs index 5ba7809e1..41e6bdec2 100644 --- a/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/GetSetPixel.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; @@ -25,3 +26,4 @@ public class GetSetPixel return image[200, 200]; } } +#endif diff --git a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs index 9560d6ee7..d32e1fdd0 100644 --- a/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/General/IO/BufferedStreams.cs @@ -78,7 +78,7 @@ public class BufferedStreams public int StandardStreamRead() { int r = 0; - Stream stream = this.stream1; + MemoryStream stream = this.stream1; byte[] b = this.chunk1; for (int i = 0; i < stream.Length / 2; i++) @@ -138,7 +138,7 @@ public class BufferedStreams public int StandardStreamReadByte() { int r = 0; - Stream stream = this.stream2; + MemoryStream stream = this.stream2; for (int i = 0; i < stream.Length; i++) { @@ -205,8 +205,8 @@ public class BufferedStreams private static byte[] CreateTestBytes() { - var buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; - var random = new Random(); + byte[] buffer = new byte[Configuration.Default.StreamProcessingBufferSize * 3]; + Random random = new(); random.NextBytes(buffer); return buffer; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs index 1d83b94dc..c864e26c6 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs @@ -184,14 +184,16 @@ public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConver } } - [Benchmark] - public void PixelConverter_Rgba32_ToArgb32() - { - Span source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); - Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Destination); - - PixelConverter.FromRgba32.ToArgb32(source, dest); - } + // Commenting this out because for some reason MSBuild is showing error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.Span' + // when trying to build via BenchmarkDotnet. (╯‵□′)╯︵┻━┻ + // [Benchmark] + // public void PixelConverter_Rgba32_ToArgb32() + // { + // ReadOnlySpan source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); + // Span destination = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Destination); + // + // PixelConverter.FromRgba32.ToArgb32(source, destination); + // } /* BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) diff --git a/tests/ImageSharp.Benchmarks/General/StructCasting.cs b/tests/ImageSharp.Benchmarks/General/StructCasting.cs index f8432112e..3f3767d42 100644 --- a/tests/ImageSharp.Benchmarks/General/StructCasting.cs +++ b/tests/ImageSharp.Benchmarks/General/StructCasting.cs @@ -11,7 +11,7 @@ public class StructCasting [Benchmark(Baseline = true)] public short ExplicitCast() { - int x = 5 * 2; + const int x = 5 * 2; return (short)x; } @@ -25,6 +25,7 @@ public class StructCasting [Benchmark] public short UnsafeCastRef() { - return Unsafe.As(ref Unsafe.AsRef(5 * 2)); + int x = 5 * 2; + return Unsafe.As(ref Unsafe.AsRef(ref x)); } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs index 9c95c22e0..5277897bb 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs @@ -15,10 +15,10 @@ public class DivFloat : SIMDBenchmarkBase.Divide [Benchmark(Baseline = true)] public void Standard() { - float v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + float v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] / v; + this.Result[i] = this.Input[i] / v; } } } @@ -30,10 +30,10 @@ public class Divide : SIMDBenchmarkBase.Divide [Benchmark(Baseline = true)] public void Standard() { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + uint v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] / v; + this.Result[i] = this.Input[i] / v; } } } @@ -45,10 +45,10 @@ public class DivInt32 : SIMDBenchmarkBase.Divide [Benchmark(Baseline = true)] public void Standard() { - int v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + int v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] / v; + this.Result[i] = this.Input[i] / v; } } } @@ -62,10 +62,10 @@ public class DivInt16 : SIMDBenchmarkBase.Divide [Benchmark(Baseline = true)] public void Standard() { - short v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + short v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = (short)(this.input[i] / v); + this.Result[i] = (short)(this.Input[i] / v); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs index fe48c3301..7e890cb92 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs @@ -15,10 +15,10 @@ public class MulUInt32 : SIMDBenchmarkBase.Multiply [Benchmark(Baseline = true)] public void Standard() { - uint v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + uint v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] * v; + this.Result[i] = this.Input[i] * v; } } } @@ -28,10 +28,10 @@ public class MulInt32 : SIMDBenchmarkBase.Multiply [Benchmark(Baseline = true)] public void Standard() { - int v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + int v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = this.input[i] * v; + this.Result[i] = this.Input[i] * v; } } } @@ -45,10 +45,10 @@ public class MulInt16 : SIMDBenchmarkBase.Multiply [Benchmark(Baseline = true)] public void Standard() { - short v = this.testValue; - for (int i = 0; i < this.input.Length; i++) + short v = this.TestValue; + for (int i = 0; i < this.Input.Length; i++) { - this.result[i] = (short)(this.input[i] * v); + this.Result[i] = (short)(this.Input[i] * v); } } } diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs index 90d81a058..0d856df61 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs @@ -10,28 +10,28 @@ namespace ImageSharp.Benchmarks.General.Vectorization; public abstract class SIMDBenchmarkBase where T : struct { - protected T[] input; + protected virtual T GetTestValue() => default; - protected T[] result; + protected virtual Vector GetTestVector() => new(this.GetTestValue()); - protected T testValue; + [Params(32)] + public int InputSize { get; set; } - protected Vector testVector; + protected T[] Input { get; set; } - protected virtual T GetTestValue() => default; + protected T[] Result { get; set; } - protected virtual Vector GetTestVector() => new Vector(this.GetTestValue()); + protected T TestValue { get; set; } - [Params(32)] - public int InputSize { get; set; } + protected Vector TestVector { get; set; } [GlobalSetup] public virtual void Setup() { - this.input = new T[this.InputSize]; - this.result = new T[this.InputSize]; - this.testValue = this.GetTestValue(); - this.testVector = this.GetTestVector(); + this.Input = new T[this.InputSize]; + this.Result = new T[this.InputSize]; + this.TestValue = this.GetTestValue(); + this.TestVector = this.GetTestVector(); } public abstract class Multiply : SIMDBenchmarkBase @@ -39,13 +39,13 @@ public abstract class SIMDBenchmarkBase [Benchmark] public void Simd() { - Vector v = this.testVector; + Vector v = this.TestVector; - for (int i = 0; i < this.input.Length; i += Vector.Count) + for (int i = 0; i < this.Input.Length; i += Vector.Count) { - Vector a = Unsafe.As>(ref this.input[i]); - a = a * v; - Unsafe.As>(ref this.result[i]) = a; + Vector a = Unsafe.As>(ref this.Input[i]); + a *= v; + Unsafe.As>(ref this.Result[i]) = a; } } } @@ -55,13 +55,13 @@ public abstract class SIMDBenchmarkBase [Benchmark] public void Simd() { - Vector v = this.testVector; + Vector v = this.TestVector; - for (int i = 0; i < this.input.Length; i += Vector.Count) + for (int i = 0; i < this.Input.Length; i += Vector.Count) { - Vector a = Unsafe.As>(ref this.input[i]); - a = a / v; - Unsafe.As>(ref this.result[i]) = a; + Vector a = Unsafe.As>(ref this.Input[i]); + a /= v; + Unsafe.As>(ref this.Result[i]) = a; } } } diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 37a991248..c92bb6a6b 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -15,9 +15,25 @@ - + + - CA1822 + + + + CA1822;CA1416;CA1001;CS0029;CA1861;CA2201 + + + + + + + + + diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index e7d240acd..af9fb5544 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; @@ -40,7 +41,7 @@ public class LoadResizeSaveStressRunner public double TotalProcessedMegapixels { get; private set; } - public Size LastProcessedImageSize { get; private set; } + public ImageSharpSize LastProcessedImageSize { get; private set; } private string outputDirectory; @@ -345,3 +346,4 @@ public class LoadResizeSaveStressRunner thumb.Jpegsave(this.OutputPath(input), q: Quality, keep: NetVips.Enums.ForeignKeep.None); } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Processing/Crop.cs b/tests/ImageSharp.Benchmarks/Processing/Crop.cs index 0432b7624..67da1076f 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Crop.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; @@ -17,9 +18,9 @@ public class Crop [Benchmark(Baseline = true, Description = "System.Drawing Crop")] public SDSize CropSystemDrawing() { - using var source = new Bitmap(800, 800); - using var destination = new Bitmap(100, 100); - using var graphics = Graphics.FromImage(destination); + using Bitmap source = new(800, 800); + using Bitmap destination = new(100, 100); + using Graphics graphics = Graphics.FromImage(destination); graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; @@ -32,8 +33,9 @@ public class Crop [Benchmark(Description = "ImageSharp Crop")] public Size CropImageSharp() { - using var image = new Image(800, 800); + using Image image = new(800, 800); image.Mutate(x => x.Crop(100, 100)); return new Size(image.Width, image.Height); } } +#endif diff --git a/tests/ImageSharp.Benchmarks/Processing/Resize.cs b/tests/ImageSharp.Benchmarks/Processing/Resize.cs index 05baceb6a..9a3b7f061 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Resize.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +#if OS_WINDOWS using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks; public abstract class Resize where TPixel : unmanaged, IPixel { - private byte[] bytes = null; + private byte[] bytes; private Image sourceImage; @@ -35,7 +36,7 @@ public abstract class Resize this.sourceImage = Image.Load(this.bytes); - var ms1 = new MemoryStream(this.bytes); + MemoryStream ms1 = new(this.bytes); this.sourceBitmap = SDImage.FromStream(ms1); this.DestSize = this.sourceBitmap.Width / 2; } @@ -52,21 +53,19 @@ public abstract class Resize [Benchmark(Baseline = true)] public int SystemDrawing() { - using (var destination = new Bitmap(this.DestSize, this.DestSize)) + using Bitmap destination = new(this.DestSize, this.DestSize); + using (Graphics g = Graphics.FromImage(destination)) { - using (var g = Graphics.FromImage(destination)) - { - g.CompositingMode = CompositingMode.SourceCopy; - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.PixelOffsetMode = PixelOffsetMode.HighQuality; - g.CompositingQuality = CompositingQuality.HighQuality; - g.SmoothingMode = SmoothingMode.HighQuality; - - g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize); - } - - return destination.Width; + g.CompositingMode = CompositingMode.SourceCopy; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.SmoothingMode = SmoothingMode.HighQuality; + + g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize); } + + return destination.Width; } [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")] @@ -87,10 +86,8 @@ public abstract class Resize { this.Configuration.MaxDegreeOfParallelism = maxDegreeOfParallelism; - using (Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation)) - { - return clone.Width; - } + using Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation); + return clone.Width; } protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); @@ -244,3 +241,4 @@ public class Resize_Bicubic_Compare_Rgba32_Rgb24 [Benchmark] public void Rgb24() => this.rgb24.ImageSharp_P1(); } +#endif From 3448ea42f81d1ded8efeaa504e6f2f2d6def6f71 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 10 Feb 2025 15:47:49 +1000 Subject: [PATCH 5/9] Don't exclude, just suppress. --- tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs | 2 -- tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs | 2 -- .../Codecs/Bmp/EncodeBmpMultiple.cs | 2 -- tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs | 12 ++---------- tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs | 2 -- .../Codecs/Gif/EncodeGifMultiple.cs | 2 -- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 3 +-- .../Codecs/Jpeg/DecodeJpeg_Aggregate.cs | 2 -- .../Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs | 2 -- .../Codecs/MultiImageBenchmarkBase.cs | 2 -- tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs | 2 -- tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs | 2 -- tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs | 2 +- .../ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs | 2 -- .../ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs | 2 -- .../ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs | 4 ++-- .../ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs | 2 -- tests/ImageSharp.Benchmarks/Color/ColorEquality.cs | 2 -- tests/ImageSharp.Benchmarks/Config.cs | 6 ++---- .../LoadResizeSave/LoadResizeSaveStressRunner.cs | 2 -- tests/ImageSharp.Benchmarks/Processing/Crop.cs | 2 -- tests/ImageSharp.Benchmarks/Processing/Resize.cs | 2 -- 22 files changed, 8 insertions(+), 53 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs index d56cffd35..eec926a23 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/DecodeBmp.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -41,4 +40,3 @@ public class DecodeBmp return new Size(image.Width, image.Height); } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs index e61f31499..4c0a6c47b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmp.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; @@ -52,4 +51,3 @@ public class EncodeBmp this.bmpCore.SaveAsBmp(memoryStream); } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs index 457abbf0e..fdef1b2bf 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Bmp/EncodeBmpMultiple.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Bmp; @@ -29,4 +28,3 @@ public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded return null; }); } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs index 8c255b61a..117cdd25b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/DecodeGif.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -19,15 +18,9 @@ public class DecodeGif => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); [GlobalSetup] - public void ReadImages() - { - if (this.gifBytes == null) - { - this.gifBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } + public void ReadImages() => this.gifBytes ??= File.ReadAllBytes(this.TestImageFullPath); - [Params(TestImages.Gif.Rings)] + [Params(TestImages.Gif.Cheers)] public string TestImage { get; set; } [Benchmark(Baseline = true, Description = "System.Drawing Gif")] @@ -46,4 +39,3 @@ public class DecodeGif return new Size(image.Width, image.Height); } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs index 46ced0351..beedbbe07 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGif.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Gif; @@ -65,4 +64,3 @@ public class EncodeGif this.bmpCore.SaveAsGif(memoryStream, this.encoder); } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs index 7bd1eb414..303272837 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Gif/EncodeGifMultiple.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Gif; @@ -40,4 +39,3 @@ public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded return null; }); } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index cf35ebbaa..5f6dbbdf7 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; @@ -66,7 +65,7 @@ public class DecodeJpegParseStreamOnly } } } -#endif + /* BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1083 (20H2/October2020Update) Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs index 76b6d27bd..9f69f613e 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -38,4 +37,3 @@ public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase public void SystemDrawing() => this.ForEachStream(SDImage.FromStream); } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index cf3c8a434..257e44cc4 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Tests; @@ -67,4 +66,3 @@ public class DecodeJpeg_ImageSpecific | 'Decode Jpeg - ImageSharp' | Jpg/i(...)e.jpg [43] | 276.490 ms | 195.5104 ms | 10.7166 ms | 0.71 | 0.01 | - | - | - | 36022368 B | */ } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index 8bb0522ab..0adc52441 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing; using System.Numerics; using BenchmarkDotNet.Attributes; @@ -237,4 +236,3 @@ public abstract class MultiImageBenchmarkBase } } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs index 0a6b880b7..2cf62fccf 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/DecodePng.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; @@ -41,4 +40,3 @@ public class DecodePng return image.Size; } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs index 50935efcd..30a10af09 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Png/EncodePng.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Png; @@ -59,4 +58,3 @@ public class EncodePng this.bmpCore.SaveAsPng(memoryStream, encoder); } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs index a56b733f0..d6a6cf1fb 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs @@ -31,7 +31,7 @@ public class DecodeTga { MagickReadSettings settings = new() { Format = MagickFormat.Tga }; using MagickImage image = new(new MemoryStream(this.data), settings); - return (int)image.Width; + return image.Width; } [Benchmark(Description = "ImageSharp Tga")] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs index 9beed63b9..ecb87e16c 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS // Enable this for using larger Tiff files. Those files are very large (> 700MB) and therefor not part of the git repo. // Use the scripts gen_big.ps1 and gen_medium.ps1 in tests\Images\Input\Tiff\Benchmarks to generate those images. //// #define BIG_TESTS @@ -83,4 +82,3 @@ public class DecodeTiff return image.Size; } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs index 67c12773a..6fa6a15ed 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; @@ -111,4 +110,3 @@ public class EncodeTiff public static bool IsOneBitCompression(TiffCompression compression) => compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax; } -#endif diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs index 4d6252c2b..bba1bc187 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs @@ -47,7 +47,7 @@ public class DecodeWebp MagickReadSettings settings = new() { Format = MagickFormat.WebP }; using MemoryStream memoryStream = new(this.webpLossyBytes); using MagickImage image = new(memoryStream, settings); - return (int)image.Width; + return image.Width; } [Benchmark(Description = "ImageSharp Lossy Webp")] @@ -65,7 +65,7 @@ public class DecodeWebp { Format = MagickFormat.WebP }; using MemoryStream memoryStream = new(this.webpLossyBytes); using MagickImage image = new(memoryStream, settings); - return (int)image.Width; + return image.Width; } [Benchmark(Description = "ImageSharp Lossless Webp")] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs index 31b6cbdde..3b9058498 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs @@ -14,9 +14,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs; [MarkdownExporter] [HtmlExporter] [Config(typeof(Config.Short))] -#pragma warning disable CA1001 // Types that own disposable fields should be disposable public class EncodeWebp -#pragma warning restore CA1001 // Types that own disposable fields should be disposable { private MagickImage webpMagick; private Image webp; diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index de6cc420e..5166c89a9 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SystemColor = System.Drawing.Color; @@ -18,4 +17,3 @@ public class ColorEquality public bool ColorEqual() => new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); } -#endif diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 06e857484..190c245c9 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -32,7 +32,7 @@ public partial class Config : ManualConfig public class Standard : Config { public Standard() => this.AddJob( - Job.Default.WithRuntime(CoreRuntime.Core80).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + Job.Default.WithRuntime(CoreRuntime.Core80).WithArguments([new MsBuildArgument("/p:DebugType=portable")])); } public class Short : Config @@ -42,12 +42,10 @@ public partial class Config : ManualConfig .WithLaunchCount(1) .WithWarmupCount(3) .WithIterationCount(3) - .WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + .WithArguments([new MsBuildArgument("/p:DebugType=portable")])); } #if OS_WINDOWS -#pragma warning disable CA1416 // Validate platform compatibility private bool IsElevated => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); -#pragma warning restore CA1416 // Validate platform compatibility #endif } diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs index af9fb5544..44c248dc9 100644 --- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs +++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; @@ -346,4 +345,3 @@ public class LoadResizeSaveStressRunner thumb.Jpegsave(this.OutputPath(input), q: Quality, keep: NetVips.Enums.ForeignKeep.None); } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Processing/Crop.cs b/tests/ImageSharp.Benchmarks/Processing/Crop.cs index 67da1076f..e14366bfd 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Crop.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; @@ -38,4 +37,3 @@ public class Crop return new Size(image.Width, image.Height); } } -#endif diff --git a/tests/ImageSharp.Benchmarks/Processing/Resize.cs b/tests/ImageSharp.Benchmarks/Processing/Resize.cs index 9a3b7f061..09673cb96 100644 --- a/tests/ImageSharp.Benchmarks/Processing/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Processing/Resize.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#if OS_WINDOWS using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; @@ -241,4 +240,3 @@ public class Resize_Bicubic_Compare_Rgba32_Rgb24 [Benchmark] public void Rgb24() => this.rgb24.ImageSharp_P1(); } -#endif From 29f17eb05e89c60ceef61573a8f3460b3e11cd58 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 10 Feb 2025 16:20:14 +1000 Subject: [PATCH 6/9] Fix #2859 --- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 325 +++++++++++------- .../Formats/Gif/GifDecoderTests.cs | 12 + tests/ImageSharp.Tests/TestImages.cs | 2 + .../00.png | 3 + .../00.png | 3 + .../01.png | 3 + .../Images/Input/Gif/issues/issue_2859_A.gif | 3 + .../Images/Input/Gif/issues/issue_2859_B.gif | 3 + 8 files changed, 222 insertions(+), 132 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_A.gif/00.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/00.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/01.png create mode 100644 tests/Images/Input/Gif/issues/issue_2859_A.gif create mode 100644 tests/Images/Input/Gif/issues/issue_2859_B.gif diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index ec33f2b3e..b33d1f3d4 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -3,7 +3,6 @@ using System.Buffers; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -37,22 +36,22 @@ internal sealed class LzwDecoder : IDisposable /// /// The prefix buffer. /// - private readonly IMemoryOwner prefix; + private readonly IMemoryOwner prefixOwner; /// /// The suffix buffer. /// - private readonly IMemoryOwner suffix; + private readonly IMemoryOwner suffixOwner; /// /// The scratch buffer for reading data blocks. /// - private readonly IMemoryOwner scratchBuffer; + private readonly IMemoryOwner bufferOwner; /// /// The pixel stack buffer. /// - private readonly IMemoryOwner pixelStack; + private readonly IMemoryOwner pixelStackOwner; private readonly int minCodeSize; private readonly int clearCode; private readonly int endCode; @@ -79,11 +78,12 @@ internal sealed class LzwDecoder : IDisposable public LzwDecoder(MemoryAllocator memoryAllocator, BufferedReadStream stream, int minCodeSize) { this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); + Guard.IsTrue(IsValidMinCodeSize(minCodeSize), nameof(minCodeSize), "Invalid minimum code size."); - this.prefix = memoryAllocator.Allocate(MaxStackSize, AllocationOptions.Clean); - this.suffix = memoryAllocator.Allocate(MaxStackSize, AllocationOptions.Clean); - this.pixelStack = memoryAllocator.Allocate(MaxStackSize + 1, AllocationOptions.Clean); - this.scratchBuffer = memoryAllocator.Allocate(byte.MaxValue, AllocationOptions.None); + this.prefixOwner = memoryAllocator.Allocate(MaxStackSize, AllocationOptions.Clean); + this.suffixOwner = memoryAllocator.Allocate(MaxStackSize, AllocationOptions.Clean); + this.pixelStackOwner = memoryAllocator.Allocate(MaxStackSize + 1, AllocationOptions.Clean); + this.bufferOwner = memoryAllocator.Allocate(byte.MaxValue, AllocationOptions.None); this.minCodeSize = minCodeSize; // Calculate the clear code. The value of the clear code is 2 ^ minCodeSize @@ -93,11 +93,15 @@ internal sealed class LzwDecoder : IDisposable this.endCode = this.clearCode + 1; this.availableCode = this.clearCode + 2; - ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); - for (this.code = 0; this.code < this.clearCode; this.code++) + // Fill the suffix buffer with the initial values represented by the number of colors. + Span suffix = this.suffixOwner.GetSpan()[..this.clearCode]; + int i; + for (i = 0; i < suffix.Length; i++) { - Unsafe.Add(ref suffixRef, (uint)this.code) = (byte)this.code; + suffix[i] = i; } + + this.code = i; } /// @@ -112,8 +116,7 @@ internal sealed class LzwDecoder : IDisposable // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - int clearCode = 1 << minCodeSize; - if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) + if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || 1 << minCodeSize > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided @@ -132,112 +135,139 @@ internal sealed class LzwDecoder : IDisposable { indices.Clear(); - ref byte pixelsRowRef = ref MemoryMarshal.GetReference(indices); - ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan()); - ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); - ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan()); - Span buffer = this.scratchBuffer.GetSpan(); - - int x = 0; - int xyz = 0; - while (xyz < indices.Length) + // Get span values from the owners. + Span prefix = this.prefixOwner.GetSpan(); + Span suffix = this.suffixOwner.GetSpan(); + Span pixelStack = this.pixelStackOwner.GetSpan(); + Span buffer = this.bufferOwner.GetSpan(); + + // Cache frequently accessed instance fields into locals. + // This helps avoid repeated field loads inside the tight loop. + BufferedReadStream stream = this.stream; + int top = this.top; + int bits = this.bits; + int codeSize = this.codeSize; + int codeMask = this.codeMask; + int minCodeSize = this.minCodeSize; + int availableCode = this.availableCode; + int oldCode = this.oldCode; + int first = this.first; + int data = this.data; + int count = this.count; + int bufferIndex = this.bufferIndex; + int code = this.code; + int clearCode = this.clearCode; + int endCode = this.endCode; + + int i = 0; + while (i < indices.Length) { - if (this.top == 0) + if (top == 0) { - if (this.bits < this.codeSize) + if (bits < codeSize) { // Load bytes until there are enough bits for a code. - if (this.count == 0) + if (count == 0) { // Read a new data block. - this.count = this.ReadBlock(buffer); - if (this.count == 0) + count = ReadBlock(stream, buffer); + if (count == 0) { break; } - this.bufferIndex = 0; + bufferIndex = 0; } - this.data += buffer[this.bufferIndex] << this.bits; - - this.bits += 8; - this.bufferIndex++; - this.count--; + data += buffer[bufferIndex] << bits; + bits += 8; + bufferIndex++; + count--; continue; } // Get the next code - this.code = this.data & this.codeMask; - this.data >>= this.codeSize; - this.bits -= this.codeSize; + code = data & codeMask; + data >>= codeSize; + bits -= codeSize; // Interpret the code - if (this.code > this.availableCode || this.code == this.endCode) + if (code > availableCode || code == endCode) { break; } - if (this.code == this.clearCode) + if (code == clearCode) { // Reset the decoder - this.codeSize = this.minCodeSize + 1; - this.codeMask = (1 << this.codeSize) - 1; - this.availableCode = this.clearCode + 2; - this.oldCode = NullCode; + codeSize = minCodeSize + 1; + codeMask = (1 << codeSize) - 1; + availableCode = clearCode + 2; + oldCode = NullCode; continue; } - if (this.oldCode == NullCode) + if (oldCode == NullCode) { - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); - this.oldCode = this.code; - this.first = this.code; + pixelStack[top++] = suffix[code]; + oldCode = code; + first = code; continue; } - int inCode = this.code; - if (this.code == this.availableCode) + int inCode = code; + if (code == availableCode) { - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = (byte)this.first; - - this.code = this.oldCode; + pixelStack[top++] = first; + code = oldCode; } - while (this.code > this.clearCode) + while (code > clearCode && top < MaxStackSize) { - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); - this.code = Unsafe.Add(ref prefixRef, (uint)this.code); + pixelStack[top++] = suffix[code]; + code = prefix[code]; } - int suffixCode = Unsafe.Add(ref suffixRef, (uint)this.code); - this.first = suffixCode; - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = suffixCode; + int suffixCode = suffix[code]; + first = suffixCode; + pixelStack[top++] = suffixCode; - // Fix for Gifs that have "deferred clear code" as per here : + // Fix for GIFs that have "deferred clear code" as per: // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 - if (this.availableCode < MaxStackSize) + if (availableCode < MaxStackSize) { - Unsafe.Add(ref prefixRef, (uint)this.availableCode) = this.oldCode; - Unsafe.Add(ref suffixRef, (uint)this.availableCode) = this.first; - this.availableCode++; - if (this.availableCode == this.codeMask + 1 && this.availableCode < MaxStackSize) + prefix[availableCode] = oldCode; + suffix[availableCode] = first; + availableCode++; + if (availableCode == codeMask + 1 && availableCode < MaxStackSize) { - this.codeSize++; - this.codeMask = (1 << this.codeSize) - 1; + codeSize++; + codeMask = (1 << codeSize) - 1; } } - this.oldCode = inCode; + oldCode = inCode; } // Pop a pixel off the pixel stack. - this.top--; + top--; - // Clear missing pixels - xyz++; - Unsafe.Add(ref pixelsRowRef, (uint)x++) = (byte)Unsafe.Add(ref pixelStackRef, (uint)this.top); + // Clear missing pixels. + indices[i++] = (byte)pixelStack[top]; } + + // Write back the local values to the instance fields. + this.top = top; + this.bits = bits; + this.codeSize = codeSize; + this.codeMask = codeMask; + this.availableCode = availableCode; + this.oldCode = oldCode; + this.first = first; + this.data = data; + this.count = count; + this.bufferIndex = bufferIndex; + this.code = code; } /// @@ -246,130 +276,161 @@ internal sealed class LzwDecoder : IDisposable /// The resulting index table length. public void SkipIndices(int length) { - ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan()); - ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); - ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan()); - Span buffer = this.scratchBuffer.GetSpan(); - - int xyz = 0; - while (xyz < length) + // Get span values from the owners. + Span prefix = this.prefixOwner.GetSpan(); + Span suffix = this.suffixOwner.GetSpan(); + Span pixelStack = this.pixelStackOwner.GetSpan(); + Span buffer = this.bufferOwner.GetSpan(); + + // Cache frequently accessed instance fields into locals. + // This helps avoid repeated field loads inside the tight loop. + BufferedReadStream stream = this.stream; + int top = this.top; + int bits = this.bits; + int codeSize = this.codeSize; + int codeMask = this.codeMask; + int minCodeSize = this.minCodeSize; + int availableCode = this.availableCode; + int oldCode = this.oldCode; + int first = this.first; + int data = this.data; + int count = this.count; + int bufferIndex = this.bufferIndex; + int code = this.code; + int clearCode = this.clearCode; + int endCode = this.endCode; + + int i = 0; + while (i < length) { - if (this.top == 0) + if (top == 0) { - if (this.bits < this.codeSize) + if (bits < codeSize) { // Load bytes until there are enough bits for a code. - if (this.count == 0) + if (count == 0) { // Read a new data block. - this.count = this.ReadBlock(buffer); - if (this.count == 0) + count = ReadBlock(stream, buffer); + if (count == 0) { break; } - this.bufferIndex = 0; + bufferIndex = 0; } - this.data += buffer[this.bufferIndex] << this.bits; - - this.bits += 8; - this.bufferIndex++; - this.count--; + data += buffer[bufferIndex] << bits; + bits += 8; + bufferIndex++; + count--; continue; } // Get the next code - this.code = this.data & this.codeMask; - this.data >>= this.codeSize; - this.bits -= this.codeSize; + code = data & codeMask; + data >>= codeSize; + bits -= codeSize; // Interpret the code - if (this.code > this.availableCode || this.code == this.endCode) + if (code > availableCode || code == endCode) { break; } - if (this.code == this.clearCode) + if (code == clearCode) { // Reset the decoder - this.codeSize = this.minCodeSize + 1; - this.codeMask = (1 << this.codeSize) - 1; - this.availableCode = this.clearCode + 2; - this.oldCode = NullCode; + codeSize = minCodeSize + 1; + codeMask = (1 << codeSize) - 1; + availableCode = clearCode + 2; + oldCode = NullCode; continue; } - if (this.oldCode == NullCode) + if (oldCode == NullCode) { - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); - this.oldCode = this.code; - this.first = this.code; + pixelStack[top++] = suffix[code]; + oldCode = code; + first = code; continue; } - int inCode = this.code; - if (this.code == this.availableCode) + int inCode = code; + if (code == availableCode) { - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = (byte)this.first; - - this.code = this.oldCode; + pixelStack[top++] = first; + code = oldCode; } - while (this.code > this.clearCode) + while (code > clearCode && top < MaxStackSize) { - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); - this.code = Unsafe.Add(ref prefixRef, (uint)this.code); + pixelStack[top++] = suffix[code]; + code = prefix[code]; } - int suffixCode = Unsafe.Add(ref suffixRef, (uint)this.code); - this.first = suffixCode; - Unsafe.Add(ref pixelStackRef, (uint)this.top++) = suffixCode; + int suffixCode = suffix[code]; + first = suffixCode; + pixelStack[top++] = suffixCode; - // Fix for Gifs that have "deferred clear code" as per here : + // Fix for GIFs that have "deferred clear code" as per: // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 - if (this.availableCode < MaxStackSize) + if (availableCode < MaxStackSize) { - Unsafe.Add(ref prefixRef, (uint)this.availableCode) = this.oldCode; - Unsafe.Add(ref suffixRef, (uint)this.availableCode) = this.first; - this.availableCode++; - if (this.availableCode == this.codeMask + 1 && this.availableCode < MaxStackSize) + prefix[availableCode] = oldCode; + suffix[availableCode] = first; + availableCode++; + if (availableCode == codeMask + 1 && availableCode < MaxStackSize) { - this.codeSize++; - this.codeMask = (1 << this.codeSize) - 1; + codeSize++; + codeMask = (1 << codeSize) - 1; } } - this.oldCode = inCode; + oldCode = inCode; } // Pop a pixel off the pixel stack. - this.top--; + top--; - // Clear missing pixels - xyz++; + // Skip missing pixels. + i++; } + + // Write back the local values to the instance fields. + this.top = top; + this.bits = bits; + this.codeSize = codeSize; + this.codeMask = codeMask; + this.availableCode = availableCode; + this.oldCode = oldCode; + this.first = first; + this.data = data; + this.count = count; + this.bufferIndex = bufferIndex; + this.code = code; } /// /// Reads the next data block from the stream. A data block begins with a byte, /// which defines the size of the block, followed by the block itself. /// + /// The stream to read from. /// The buffer to store the block in. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ReadBlock(Span buffer) + private static int ReadBlock(BufferedReadStream stream, Span buffer) { - int bufferSize = this.stream.ReadByte(); + int bufferSize = stream.ReadByte(); if (bufferSize < 1) { return 0; } - int count = this.stream.Read(buffer, 0, bufferSize); + int count = stream.Read(buffer, 0, bufferSize); return count != bufferSize ? 0 : bufferSize; } @@ -377,9 +438,9 @@ internal sealed class LzwDecoder : IDisposable /// public void Dispose() { - this.prefix.Dispose(); - this.suffix.Dispose(); - this.pixelStack.Dispose(); - this.scratchBuffer.Dispose(); + this.prefixOwner.Dispose(); + this.suffixOwner.Dispose(); + this.pixelStackOwner.Dispose(); + this.bufferOwner.Dispose(); } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index f4e6487a5..bc6eeedcb 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -334,4 +334,16 @@ public class GifDecoderTests image.DebugSaveMultiFrame(provider); image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } + + // https://github.com/SixLabors/ImageSharp/issues/2859 + [Theory] + [WithFile(TestImages.Gif.Issues.Issue2859_A, PixelTypes.Rgba32)] + [WithFile(TestImages.Gif.Issues.Issue2859_B, PixelTypes.Rgba32)] + public void Issue2859_LZWPixelStackOverflow(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 4130474b5..fafa1d242 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -536,6 +536,8 @@ public static class TestImages public const string Issue2450_B = "Gif/issues/issue_2450_2.gif"; public const string Issue2198 = "Gif/issues/issue_2198.gif"; public const string Issue2758 = "Gif/issues/issue_2758.gif"; + public const string Issue2859_A = "Gif/issues/issue_2859_A.gif"; + public const string Issue2859_B = "Gif/issues/issue_2859_B.gif"; } public static readonly string[] Animated = diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_A.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_A.gif/00.png new file mode 100644 index 000000000..d8c8df126 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_A.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:daa78347749c6ff49891e2e379a373599cd35c98b453af9bf8eac52f615f935c +size 12237 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/00.png new file mode 100644 index 000000000..36c368318 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:731299281f942f277ce6803e0adda3b5dd0395eb79cae26cabc9d56905fae0fd +size 1833 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/01.png new file mode 100644 index 000000000..c03e5817f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2859_LZWPixelStackOverflow_Rgba32_issue_2859_B.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50ccac7739142578d99a76b6d39ba377099d4a7ac30cbb0a5aee44ef1e7c9c8c +size 1271 diff --git a/tests/Images/Input/Gif/issues/issue_2859_A.gif b/tests/Images/Input/Gif/issues/issue_2859_A.gif new file mode 100644 index 000000000..f19a04752 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2859_A.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50a1a4afc62a3a36ff83596f1eb55d91cdd184c64e0d1339bbea17205c23eee1 +size 3406142 diff --git a/tests/Images/Input/Gif/issues/issue_2859_B.gif b/tests/Images/Input/Gif/issues/issue_2859_B.gif new file mode 100644 index 000000000..109b0f879 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2859_B.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db9b2992be772a4f0ac495e994a17c7c50fb6de9794cfb9afc4a3dc26ffdfae0 +size 4543 From b2c03c1799b2253468eb2245a1c3f854ca47a17a Mon Sep 17 00:00:00 2001 From: Paulo Morgado <470455+paulomorgado@users.noreply.github.com> Date: Wed, 26 Mar 2025 12:17:25 +0000 Subject: [PATCH 7/9] Refactor buffer allocation in Vp8BitWriter.cs to use Span instead of byte[] in WriteFrameHeader. This change enhances memory efficiency by reducing heap allocations and leveraging stack allocation for better performance. --- src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs index 81530706d..505f54312 100644 --- a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs +++ b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs @@ -604,7 +604,7 @@ internal class Vp8BitWriter : BitWriterBase uint profile = 0; int width = this.enc.Width; int height = this.enc.Height; - byte[] vp8FrameHeader = new byte[WebpConstants.Vp8FrameHeaderSize]; + Span vp8FrameHeader = stackalloc byte[WebpConstants.Vp8FrameHeaderSize]; // Paragraph 9.1. uint bits = 0 // keyframe (1b) From 289470aacab5425fefeb48d84e8175274e149cf5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 7 Apr 2025 21:50:06 +1000 Subject: [PATCH 8/9] Fix build --- Directory.Build.props | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 26b3cc5af..755cbe3b3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -21,9 +21,8 @@ - - - preview + + 12.0