From 5078f1e1df1d0a91a0bf61af12ca26aad602d56c Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Thu, 26 Jan 2023 09:26:19 +0100 Subject: [PATCH 01/47] Remove Nullable disable from MetaData.Profiles #2231 --- .../Formats/Gif/MetadataExtensions.cs | 3 ++- src/ImageSharp/Metadata/ImageFrameMetadata.cs | 15 +++++++------- .../Metadata/Profiles/IPTC/IptcProfile.cs | 20 +++++++++---------- .../Metadata/Profiles/IPTC/IptcValue.cs | 10 +++------- .../Metadata/Profiles/XMP/XmpProfile.cs | 13 ++++++------ 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs index 7280024e21..e20b9dd177 100644 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Metadata; @@ -37,5 +38,5 @@ public static partial class MetadataExtensions /// /// if the gif frame metadata exists; otherwise, . /// - public static bool TryGetGifMetadata(this ImageFrameMetadata source, out GifFrameMetadata metadata) => source.TryGetFormatMetadata(GifFormat.Instance, out metadata); + public static bool TryGetGifMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out GifFrameMetadata? metadata) => source.TryGetFormatMetadata(GifFormat.Instance, out metadata); } diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 08a3dc74d6..03f628afa3 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -49,22 +48,22 @@ public sealed class ImageFrameMetadata : IDeepCloneable /// /// Gets or sets the Exif profile. /// - public ExifProfile ExifProfile { get; set; } + public ExifProfile? ExifProfile { get; set; } /// /// Gets or sets the XMP profile. /// - public XmpProfile XmpProfile { get; set; } + public XmpProfile? XmpProfile { get; set; } /// /// Gets or sets the ICC profile. /// - public IccProfile IccProfile { get; set; } + public IccProfile? IccProfile { get; set; } /// /// Gets or sets the iptc profile. /// - public IptcProfile IptcProfile { get; set; } + public IptcProfile? IptcProfile { get; set; } /// public ImageFrameMetadata DeepClone() => new(this); @@ -83,7 +82,7 @@ public sealed class ImageFrameMetadata : IDeepCloneable where TFormatMetadata : class where TFormatFrameMetadata : class, IDeepCloneable { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta)) + if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) { return (TFormatFrameMetadata)meta; } @@ -107,11 +106,11 @@ public sealed class ImageFrameMetadata : IDeepCloneable /// /// if the frame metadata exists for the specified key; otherwise, . /// - public bool TryGetFormatMetadata(IImageFormat key, out TFormatFrameMetadata metadata) + public bool TryGetFormatMetadata(IImageFormat key, out TFormatFrameMetadata? metadata) where TFormatMetadata : class where TFormatFrameMetadata : class, IDeepCloneable { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable meta)) + if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) { metadata = (TFormatFrameMetadata)meta; return true; diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index 1aff9cc5d4..4c20f6c15e 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -1,9 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers.Binary; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using SixLabors.ImageSharp.Metadata.Profiles.IPTC; @@ -30,7 +30,7 @@ public sealed class IptcProfile : IDeepCloneable /// Initializes a new instance of the class. /// public IptcProfile() - : this((byte[])null) + : this((byte[]?)null) { } @@ -38,7 +38,7 @@ public sealed class IptcProfile : IDeepCloneable /// Initializes a new instance of the class. /// /// The byte array to read the iptc profile from. - public IptcProfile(byte[] data) + public IptcProfile(byte[]? data) { this.Data = data; this.Initialize(); @@ -53,14 +53,11 @@ public sealed class IptcProfile : IDeepCloneable { Guard.NotNull(other, nameof(other)); - if (other.values != null) - { - this.values = new Collection(); + this.values = new Collection(); - foreach (IptcValue value in other.Values) - { - this.values.Add(value.DeepClone()); - } + foreach (IptcValue value in other.Values) + { + this.values.Add(value.DeepClone()); } if (other.Data != null) @@ -78,7 +75,7 @@ public sealed class IptcProfile : IDeepCloneable /// /// Gets the byte data of the IPTC profile. /// - public byte[] Data { get; private set; } + public byte[]? Data { get; private set; } /// /// Gets the values of this iptc profile. @@ -310,6 +307,7 @@ public sealed class IptcProfile : IDeepCloneable return offset; } + [MemberNotNull(nameof(values))] private void Initialize() { if (this.values != null) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs index d39697e890..1a75ecba22 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Text; @@ -22,10 +21,7 @@ public sealed class IptcValue : IDeepCloneable other.data.AsSpan().CopyTo(this.data); } - if (other.Encoding != null) - { - this.Encoding = (Encoding)other.Encoding.Clone(); - } + this.encoding = (Encoding)other.Encoding.Clone(); this.Tag = other.Tag; this.Strict = other.Strict; @@ -133,7 +129,7 @@ public sealed class IptcValue : IDeepCloneable /// /// The object to compare this with. /// True when the specified object is equal to the current . - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(this, obj)) { @@ -148,7 +144,7 @@ public sealed class IptcValue : IDeepCloneable /// /// The iptc value to compare this with. /// True when the specified iptc value is equal to the current . - public bool Equals(IptcValue other) + public bool Equals(IptcValue? other) { if (other is null) { diff --git a/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs b/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs index 5e9024cf0b..77ff35df0e 100644 --- a/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable +using System.Diagnostics; using System.Text; using System.Xml.Linq; @@ -17,7 +17,7 @@ public sealed class XmpProfile : IDeepCloneable /// Initializes a new instance of the class. /// public XmpProfile() - : this((byte[])null) + : this((byte[]?)null) { } @@ -25,7 +25,7 @@ public sealed class XmpProfile : IDeepCloneable /// Initializes a new instance of the class. /// /// The UTF8 encoded byte array to read the XMP profile from. - public XmpProfile(byte[] data) => this.Data = data; + public XmpProfile(byte[]? data) => this.Data = data; /// /// Initializes a new instance of the class @@ -42,15 +42,15 @@ public sealed class XmpProfile : IDeepCloneable /// /// Gets the XMP raw data byte array. /// - internal byte[] Data { get; private set; } + internal byte[]? Data { get; private set; } /// /// Gets the raw XML document containing the XMP profile. /// /// The - public XDocument GetDocument() + public XDocument? GetDocument() { - byte[] byteArray = this.Data; + byte[]? byteArray = this.Data; if (byteArray is null) { return null; @@ -77,6 +77,7 @@ public sealed class XmpProfile : IDeepCloneable /// The public byte[] ToByteArray() { + Guard.NotNull(this.Data); byte[] result = new byte[this.Data.Length]; this.Data.AsSpan().CopyTo(result); return result; From 2e2de452ab721c12767db27ec02c67d836d2baa2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 3 Feb 2023 23:10:37 +1000 Subject: [PATCH 02/47] Add buildjet ARM64 runners. --- .github/workflows/build-and-test.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index c033dd133c..daa524d9a6 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -32,6 +32,12 @@ jobs: sdk-preview: true runtime: -x64 codecov: false + - os: buildjet-2vcpu-ubuntu-2204 + framework: net7.0 + sdk: 7.0.x + sdk-preview: true + runtime: -x64 + codecov: false - os: ubuntu-latest framework: net6.0 sdk: 6.0.x @@ -47,6 +53,11 @@ jobs: sdk: 6.0.x runtime: -x64 codecov: false + - os: buildjet-2vcpu-ubuntu-2204 + framework: net6.0 + sdk: 6.0.x + runtime: -x64 + codecov: false runs-on: ${{matrix.options.os}} From 629cc592d5d9ee4ad13ba2d26c8f2fd848552073 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 00:03:01 +1000 Subject: [PATCH 03/47] Use correct runners --- .github/workflows/build-and-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index daa524d9a6..df424eedc3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -32,7 +32,7 @@ jobs: sdk-preview: true runtime: -x64 codecov: false - - os: buildjet-2vcpu-ubuntu-2204 + - os: buildjet-2vcpu-ubuntu-2204-arm framework: net7.0 sdk: 7.0.x sdk-preview: true @@ -53,7 +53,7 @@ jobs: sdk: 6.0.x runtime: -x64 codecov: false - - os: buildjet-2vcpu-ubuntu-2204 + - os: buildjet-2vcpu-ubuntu-2204-arm framework: net6.0 sdk: 6.0.x runtime: -x64 From 286fa4deb76ffdc9c0ee067f62fd9a556efdac89 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Feb 2023 15:55:51 +0100 Subject: [PATCH 04/47] Add libgdi+ on ubuntu --- .github/workflows/build-and-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index df424eedc3..24a6ed99dd 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -62,6 +62,10 @@ jobs: runs-on: ${{matrix.options.os}} steps: + - name: Install libgdi+, which is required for tests running on ubuntu + if: ${{ matrix.options.os == ubuntu-latest }} + 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 6eec48de54dfacd0df1a93fe465e3368833d4fd6 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 3 Feb 2023 16:39:58 +0100 Subject: [PATCH 05/47] Initialize values during declaration --- src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index 4c20f6c15e..d7bd09758b 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc; /// public sealed class IptcProfile : IDeepCloneable { - private Collection values; + private readonly Collection values = new(); private const byte IptcTagMarkerByte = 0x1c; @@ -53,8 +53,6 @@ public sealed class IptcProfile : IDeepCloneable { Guard.NotNull(other, nameof(other)); - this.values = new Collection(); - foreach (IptcValue value in other.Values) { this.values.Add(value.DeepClone()); @@ -307,16 +305,8 @@ public sealed class IptcProfile : IDeepCloneable return offset; } - [MemberNotNull(nameof(values))] private void Initialize() { - if (this.values != null) - { - return; - } - - this.values = new Collection(); - if (this.Data == null || this.Data[0] != IptcTagMarkerByte) { return; From 2d6c1746063a19a5f0b1ca2871035ad7c4d75dc3 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 3 Feb 2023 16:49:13 +0100 Subject: [PATCH 06/47] Initialize is only needed in the constructor. It would just return on the other invocations --- .../Metadata/Profiles/IPTC/IptcProfile.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index d7bd09758b..162fae96b3 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -78,14 +78,7 @@ public sealed class IptcProfile : IDeepCloneable /// /// Gets the values of this iptc profile. /// - public IEnumerable Values - { - get - { - this.Initialize(); - return this.values; - } - } + public IEnumerable Values => this.values; /// public IptcProfile DeepClone() => new(this); @@ -116,8 +109,6 @@ public sealed class IptcProfile : IDeepCloneable /// True when the value was found and removed. public bool RemoveValue(IptcTag tag) { - this.Initialize(); - bool removed = false; for (int i = this.values.Count - 1; i >= 0; i--) { @@ -139,8 +130,6 @@ public sealed class IptcProfile : IDeepCloneable /// True when the value was found and removed. public bool RemoveValue(IptcTag tag, string value) { - this.Initialize(); - bool removed = false; for (int i = this.values.Count - 1; i >= 0; i--) { From fc6ec0a9f321e41c7fcb07b432663cb08bf26ce6 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Feb 2023 16:55:09 +0100 Subject: [PATCH 07/47] Install libgdi+ only for os=buildjet-2vcpu-ubuntu-2204-arm --- .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 24a6ed99dd..e632a3da58 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -63,7 +63,7 @@ jobs: steps: - name: Install libgdi+, which is required for tests running on ubuntu - if: ${{ matrix.options.os == ubuntu-latest }} + if: ${{ matrix.options.os == buildjet-2vcpu-ubuntu-2204-arm }} run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev - name: Git Config From 7963584fead04ee4c591a5c49584b4ab1e33fe8a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 12:06:39 +1000 Subject: [PATCH 08/47] Use quotes --- .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 e632a3da58..7432ace045 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -63,7 +63,7 @@ jobs: steps: - name: Install libgdi+, which is required for tests running on ubuntu - if: ${{ matrix.options.os == buildjet-2vcpu-ubuntu-2204-arm }} + if: ${{ matrix.options.os == 'buildjet-2vcpu-ubuntu-2204-arm' }} run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev - name: Git Config From 9319b26fa17eec3474b939ad2f283b96f3fbb6ef Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 12:46:22 +1000 Subject: [PATCH 09/47] Update reference encoder versions, fix environment detection --- tests/Directory.Build.props | 1 + tests/Directory.Build.targets | 4 ++-- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- .../Exceptions/ImageDifferenceIsOverThresholdException.cs | 2 +- tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs | 4 ++++ tests/ImageSharp.Tests/runtimeconfig.template.json | 5 +++++ 6 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 tests/ImageSharp.Tests/runtimeconfig.template.json diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index ac5b635962..d696acf0f2 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -18,6 +18,7 @@ ..\ImageSharp.Tests.ruleset + true diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 4e1b9503e1..c05384fcce 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -21,7 +21,7 @@ - + @@ -32,7 +32,7 @@ - + diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index c6cf347518..a6197b6009 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -4,7 +4,7 @@ True SixLabors.ImageSharp.Tests - AnyCPU;x64;x86 + AnyCPU;x64;x86;ARM64 SixLabors.ImageSharp.Tests Debug;Release diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs index 33b7228aed..5cc6d22695 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -50,7 +50,7 @@ public class ImageDifferenceIsOverThresholdException : ImagesSimilarityException return "MacOS"; } - if (TestEnvironment.IsMacOS) + if (TestEnvironment.IsLinux) { return "Linux"; } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index 113429ead7..8818830351 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -117,6 +117,10 @@ public static partial class TestEnvironment internal static bool IsFramework => NetCoreVersion == null; + internal static Architecture OSArchitecture => RuntimeInformation.OSArchitecture; + + internal static Architecture ProcessArchitecture => RuntimeInformation.ProcessArchitecture; + /// /// A dummy operation to enforce the execution of the static constructor. /// diff --git a/tests/ImageSharp.Tests/runtimeconfig.template.json b/tests/ImageSharp.Tests/runtimeconfig.template.json new file mode 100644 index 0000000000..4201c8a4ab --- /dev/null +++ b/tests/ImageSharp.Tests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Drawing.EnableUnixSupport": true + } +} \ No newline at end of file From cf7f808a0e02131f75c9308314adee21b864056d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 13:57:34 +1000 Subject: [PATCH 10/47] Adjust percentages for ARM (visually checked) --- .../Formats/Gif/GifDecoderTests.cs | 3 ++- .../Formats/Tga/TgaDecoderTests.cs | 3 ++- .../Formats/Tiff/TiffDecoderTests.cs | 3 ++- .../Formats/WebP/WebpDecoderTests.cs | 3 ++- ...ImageDifferenceIsOverThresholdException.cs | 26 +++++++++++-------- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 022451668b..9295dd1509 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.InteropServices; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; @@ -50,7 +51,7 @@ public class GifDecoderTests image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.0001F), + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0002F : 0.0001F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 3c9a2f8261..9b95a696fb 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.InteropServices; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tga; @@ -759,7 +760,7 @@ public class TgaDecoderTests image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.0001F), + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.00017F : 0.0001F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 05c1c5a138..1571ceae5d 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. // ReSharper disable InconsistentNaming +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Metadata; @@ -766,7 +767,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.Exact, + TestEnvironment.OSArchitecture == Architecture.Arm64 ? ImageComparer.TolerantPercentage(0.0006F) : ImageComparer.Exact, provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 003e5ad4c9..071dd77018 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.PixelFormats; @@ -366,7 +367,7 @@ public class WebpDecoderTests image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.0007F), + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0156F : 0.0007F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs index 5cc6d22695..8d6a1ffc74 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Globalization; using System.Text; namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -11,33 +12,36 @@ public class ImageDifferenceIsOverThresholdException : ImagesSimilarityException public ImageDifferenceIsOverThresholdException(IEnumerable reports) : base("Image difference is over threshold!" + StringifyReports(reports)) - { - this.Reports = reports.ToArray(); - } + => this.Reports = reports.ToArray(); private static string StringifyReports(IEnumerable reports) { - var sb = new StringBuilder(); + StringBuilder sb = new(); + + sb.Append(Environment.NewLine); + sb.AppendFormat(CultureInfo.InvariantCulture, "Test Environment OS : {0}", GetEnvironmentName()); + sb.Append(Environment.NewLine); + sb.AppendFormat(CultureInfo.InvariantCulture, "Test Environment is CI : {0}", TestEnvironment.RunsOnCI); sb.Append(Environment.NewLine); - sb.AppendFormat("Test Environment OS : {0}", GetEnvironmentName()); + sb.AppendFormat(CultureInfo.InvariantCulture, "Test Environment is .NET Core : {0}", !TestEnvironment.IsFramework); sb.Append(Environment.NewLine); - sb.AppendFormat("Test Environment is CI : {0}", TestEnvironment.RunsOnCI); + sb.AppendFormat(CultureInfo.InvariantCulture, "Test Environment is Mono : {0}", TestEnvironment.IsMono); sb.Append(Environment.NewLine); - sb.AppendFormat("Test Environment is .NET Core : {0}", !TestEnvironment.IsFramework); + sb.AppendFormat(CultureInfo.InvariantCulture, "Test Environment OS Architecture : {0}", TestEnvironment.OSArchitecture); sb.Append(Environment.NewLine); - sb.AppendFormat("Test Environment is Mono : {0}", TestEnvironment.IsMono); + sb.AppendFormat(CultureInfo.InvariantCulture, "Test Environment Process Architecture : {0}", TestEnvironment.ProcessArchitecture); sb.Append(Environment.NewLine); foreach (ImageSimilarityReport r in reports) { - sb.AppendFormat("Report ImageFrame {0}: ", r.Index); - sb.Append(r); - sb.Append(Environment.NewLine); + sb.AppendFormat(CultureInfo.InvariantCulture, "Report ImageFrame {0}: ", r.Index) + .Append(r) + .Append(Environment.NewLine); } return sb.ToString(); From b26dca85983154f6952b095f6e74ff80e1af19a6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 15:40:30 +1000 Subject: [PATCH 11/47] Fix up webp alpha compression test for ARM64 --- .../Formats/Gif/GifDecoderTests.cs | 3 + .../Formats/Tga/TgaDecoderTests.cs | 3 + .../Formats/Tiff/TiffDecoderTests.cs | 3 + .../Formats/WebP/WebpDecoderTests.cs | 3 + .../Formats/WebP/WebpEncoderTests.cs | 63 +++++++++++-------- 5 files changed, 48 insertions(+), 27 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 9295dd1509..b4facfa3fe 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -50,6 +50,9 @@ public class GifDecoderTests FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + + // Floating point differences result in minor pixel differences. + // Output have been manually verified. image.CompareToReferenceOutput( ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0002F : 0.0001F), provider, diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 9b95a696fb..8a9aaa7f59 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -759,6 +759,9 @@ public class TgaDecoderTests FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + + // Floating point differences result in minor pixel differences. + // Output have been manually verified. image.CompareToReferenceOutput( ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.00017F : 0.0001F), provider, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 1571ceae5d..75e7d39091 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -766,6 +766,9 @@ public class TiffDecoderTests : TiffDecoderBaseTester FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + + // Floating point differences result in minor pixel differences. + // Output have been manually verified. image.CompareToReferenceOutput( TestEnvironment.OSArchitecture == Architecture.Arm64 ? ImageComparer.TolerantPercentage(0.0006F) : ImageComparer.Exact, provider, diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 071dd77018..010af3fbbe 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -366,6 +366,9 @@ public class WebpDecoderTests FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + + // Floating point differences result in minor pixel differences. + // Output have been manually verified. image.CompareToReferenceOutput( ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0156F : 0.0007F), provider, diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index 4eb8b3063d..6c5fa50ff6 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -23,13 +24,13 @@ public class WebpEncoderTests public void Encode_PreserveRatio(TestImageProvider provider, WebpFileFormatType expectedFormat) where TPixel : unmanaged, IPixel { - var options = new WebpEncoder(); + WebpEncoder options = new(); using Image input = provider.GetImage(); - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); input.Save(memoryStream, options); memoryStream.Position = 0; - using var output = Image.Load(memoryStream); + using Image output = Image.Load(memoryStream); ImageMetadata meta = output.Metadata; WebpMetadata webpMetaData = meta.GetWebpMetadata(); @@ -43,7 +44,7 @@ public class WebpEncoderTests public void Encode_Lossless_WithPalette_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless, Quality = 100, @@ -61,7 +62,7 @@ public class WebpEncoderTests public void Encode_Lossless_WithDifferentQuality_Works(TestImageProvider provider, int quality) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless, Quality = quality @@ -90,7 +91,7 @@ public class WebpEncoderTests public void Encode_Lossless_WithDifferentMethodAndQuality_Works(TestImageProvider provider, WebpEncodingMethod method, int quality) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless, Method = method, @@ -107,14 +108,14 @@ public class WebpEncoderTests public void Encode_Lossless_WithBestQuality_HasExpectedSize(TestImageProvider provider, int expectedBytes) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless, Method = WebpEncodingMethod.BestQuality }; using Image image = provider.GetImage(); - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); image.Save(memoryStream, encoder); Assert.Equal(memoryStream.Length, expectedBytes); @@ -130,7 +131,7 @@ public class WebpEncoderTests public void Encode_Lossless_WithNearLosslessFlag_Works(TestImageProvider provider, int nearLosslessQuality) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless, NearLossless = true, @@ -154,7 +155,7 @@ public class WebpEncoderTests public void Encode_Lossless_WithPreserveTransparentColor_Works(TestImageProvider provider, WebpEncodingMethod method) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless, Method = method, @@ -170,9 +171,9 @@ public class WebpEncoderTests public void Encode_Lossless_OneByOnePixel_Works() { // Just make sure, encoding 1 pixel by 1 pixel does not throw an exception. - using var image = new Image(1, 1); - var encoder = new WebpEncoder() { FileFormat = WebpFileFormatType.Lossless }; - using (var memStream = new MemoryStream()) + using Image image = new(1, 1); + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless }; + using (MemoryStream memStream = new()) { image.SaveAsWebp(memStream, encoder); } @@ -185,7 +186,7 @@ public class WebpEncoderTests public void Encode_Lossy_WithDifferentQuality_Works(TestImageProvider provider, int quality) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy, Quality = quality @@ -205,7 +206,7 @@ public class WebpEncoderTests public void Encode_Lossy_WithDifferentFilterStrength_Works(TestImageProvider provider, int filterStrength) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy, FilterStrength = filterStrength @@ -225,7 +226,7 @@ public class WebpEncoderTests public void Encode_Lossy_WithDifferentSpatialNoiseShapingStrength_Works(TestImageProvider provider, int snsStrength) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy, SpatialNoiseShaping = snsStrength @@ -254,7 +255,7 @@ public class WebpEncoderTests public void Encode_Lossy_WithDifferentMethodsAndQuality_Works(TestImageProvider provider, WebpEncodingMethod method, int quality) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy, Method = method, @@ -267,11 +268,15 @@ public class WebpEncoderTests } [Theory] - [WithFile(TestImages.Png.Transparency, PixelTypes.Rgba32, 64020)] - public void Encode_Lossy_WithAlpha_Works(TestImageProvider provider, int expectedFileSize) + [WithFile(TestImages.Png.Transparency, PixelTypes.Rgba32)] + public void Encode_Lossy_WithAlpha_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + // Floating point differences result in minor pixel differences affecting compression. + // Output have been manually verified. + int expectedFileSize = TestEnvironment.OSArchitecture == Architecture.Arm64 ? 64060 : 64020; + + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy, UseAlphaCompression = false @@ -291,11 +296,15 @@ public class WebpEncoderTests } [Theory] - [WithFile(TestImages.Png.Transparency, PixelTypes.Rgba32, 16200)] - public void Encode_Lossy_WithAlphaUsingCompression_Works(TestImageProvider provider, int expectedFileSize) + [WithFile(TestImages.Png.Transparency, PixelTypes.Rgba32)] + public void Encode_Lossy_WithAlphaUsingCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel { - var encoder = new WebpEncoder() + // Floating point differences result in minor pixel differences affecting compression. + // Output have been manually verified. + int expectedFileSize = TestEnvironment.OSArchitecture == Architecture.Arm64 ? 16240 : 16200; + + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy, UseAlphaCompression = true @@ -322,7 +331,7 @@ public class WebpEncoderTests { using Image image = provider.GetImage(); - var encoder = new WebpEncoder() { FileFormat = WebpFileFormatType.Lossless }; + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossless }; image.VerifyEncoder(provider, "webp", string.Empty, encoder); } @@ -334,16 +343,16 @@ public class WebpEncoderTests { using Image image = provider.GetImage(); - var encoder = new WebpEncoder() { FileFormat = WebpFileFormatType.Lossy }; + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy }; image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.Tolerant(0.04f)); } public static void RunEncodeLossy_WithPeakImage() { - var provider = TestImageProvider.File(TestImageLossyFullPath); + TestImageProvider provider = TestImageProvider.File(TestImageLossyFullPath); using Image image = provider.GetImage(); - var encoder = new WebpEncoder() { FileFormat = WebpFileFormatType.Lossy }; + WebpEncoder encoder = new() { FileFormat = WebpFileFormatType.Lossy }; image.VerifyEncoder(provider, "webp", string.Empty, encoder, ImageComparer.Tolerant(0.04f)); } From 486cf3a27e0cc7017661095bb98cc1bd5dbf7122 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 16:05:28 +1000 Subject: [PATCH 12/47] Update TgaDecoderTests.cs --- tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 8a9aaa7f59..0bbe1984f0 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -763,7 +763,7 @@ public class TgaDecoderTests // Floating point differences result in minor pixel differences. // Output have been manually verified. image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.00017F : 0.0001F), + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0016F : 0.0001F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); From 6c29bbcfa3890bef869c3a59533ca0e0a094e449 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 17:25:35 +1000 Subject: [PATCH 13/47] Update test SDKs --- shared-infrastructure | 2 +- tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs | 2 +- tests/ImageSharp.Tests/PixelFormats/A8Tests.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shared-infrastructure b/shared-infrastructure index 9a82679e92..6c52486c51 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit 9a82679e92df9476725fd2a2038604fd412af56c +Subproject commit 6c52486c512357475cbb92bbb7c4c0af4d85b1db diff --git a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs index 1607e907bc..73c034a6be 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs @@ -17,7 +17,7 @@ public class LosslessUtilsTests float actual = LosslessUtils.CombinedShannonEntropy(x, y); - Assert.Equal(expected, actual, 5); + Assert.Equal(expected, actual, precision: 5); } private static void RunSubtractGreenTest() diff --git a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index afccdbd05d..95574a4d6c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -65,7 +65,7 @@ public class A8Tests Assert.Equal(0, actual.X); Assert.Equal(0, actual.Y); Assert.Equal(0, actual.Z); - Assert.Equal(.5F, actual.W, 2); + Assert.Equal(.5F, actual.W, precision: 2); } [Fact] @@ -81,7 +81,7 @@ public class A8Tests Assert.Equal(0, actual.X); Assert.Equal(0, actual.Y); Assert.Equal(0, actual.Z); - Assert.Equal(.5F, actual.W, 2); + Assert.Equal(.5F, actual.W, precision: 2); } [Fact] From 88104bd5c83ad513d0115288a34bb4bcaf403195 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 21:03:28 +1000 Subject: [PATCH 14/47] Skip allocator test on .NET7 ARM64 --- .../UniformUnmanagedPoolMemoryAllocatorTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index bd7c4f2bdb..784628fc61 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -261,6 +261,14 @@ public class UniformUnmanagedPoolMemoryAllocatorTests return; } +#if NET7_0_OR_GREATER + if (TestEnvironment.OSArchitecture == Architecture.Arm64) + { + // Skip on .NET7 ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + return; + } +#endif + if (!TestEnvironment.RunsOnCI) { // This may fail in local runs resulting in high memory load. From bc2548363b472134db40e9bcda5de93ae28009df Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 21:03:54 +1000 Subject: [PATCH 15/47] Tweak luminance encoding test precision --- tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 61c01b15c4..aed84a7d92 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -64,7 +64,7 @@ public partial class JpegEncoderTests { { JpegEncodingColor.Luminance, 100, 0.0175f / 100 }, { JpegEncodingColor.Luminance, 80, 0.6730f / 100 }, - { JpegEncodingColor.Luminance, 40, 0.9941f / 100 }, + { JpegEncodingColor.Luminance, 40, 0.9943f / 100 }, }; [Theory] From 75b86da0e42de7a39f975fec8eb9aaf7ef20800f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 21:24:41 +1000 Subject: [PATCH 16/47] Add logging to see if we can determine cause of TestHost crash --- ci-test.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-test.ps1 b/ci-test.ps1 index 3915ae4ccd..d9bb0211a5 100644 --- a/ci-test.ps1 +++ b/ci-test.ps1 @@ -33,5 +33,5 @@ elseif ($platform -eq '-x86' -and $targetFramework -match $netFxRegex) { } else { - dotnet test --no-build -c Release -f $targetFramework + dotnet test --no-build -c Release -f $targetFramework --blame --diag .tests\Images\ActualOutput\diaglog.txt } From 5aa28bc26fed783884b119d90197d84ea256c92b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 21:39:28 +1000 Subject: [PATCH 17/47] Skip additional allocation failure tests on .NET 7 ARM64 --- .../UniformUnmanagedPoolMemoryAllocatorTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index 784628fc61..d4284dbd2c 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -331,6 +331,14 @@ public class UniformUnmanagedPoolMemoryAllocatorTests return; } +#if NET7_0_OR_GREATER + if (TestEnvironment.OSArchitecture == Architecture.Arm64) + { + // Skip on .NET7 ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + return; + } +#endif + if (!TestEnvironment.RunsOnCI) { // This may fail in local runs resulting in high memory load. From 49382201673b5d8de33691255bc942427e24dcb3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 21:51:29 +1000 Subject: [PATCH 18/47] Try bumping up the runners to give us more ram/cpu --- .github/workflows/build-and-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 7432ace045..1d2a8e6dfc 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -32,7 +32,7 @@ jobs: sdk-preview: true runtime: -x64 codecov: false - - os: buildjet-2vcpu-ubuntu-2204-arm + - os: buildjet-4vcpu-ubuntu-2204-arm framework: net7.0 sdk: 7.0.x sdk-preview: true @@ -53,7 +53,7 @@ jobs: sdk: 6.0.x runtime: -x64 codecov: false - - os: buildjet-2vcpu-ubuntu-2204-arm + - os: buildjet-4vcpu-ubuntu-2204-arm framework: net6.0 sdk: 6.0.x runtime: -x64 @@ -63,7 +63,7 @@ jobs: steps: - name: Install libgdi+, which is required for tests running on ubuntu - if: ${{ matrix.options.os == 'buildjet-2vcpu-ubuntu-2204-arm' }} + if: ${{ matrix.options.os == 'buildjet-4vcpu-ubuntu-2204-arm' }} run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev - name: Git Config From 4551f457b069b652459e32e1b0169f2499da6939 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 22:05:16 +1000 Subject: [PATCH 19/47] Tweak Png decode/resize differences for ARM64 --- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 7e6f5138a6..afd33608ce 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Runtime.InteropServices; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; @@ -120,8 +121,11 @@ public partial class PngDecoderTests FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); + + // Floating point differences result in minor pixel differences. + // Output have been manually verified. image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.0003F), // Magick decoder shows difference on Mac + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0005F : 0.0003F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); From b158e0e2e6ed54be77048680cb9109ad5f8214bf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 22:05:33 +1000 Subject: [PATCH 20/47] Skip another allocation test on ARM64 .NET7 --- .../Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs index 2f47e6ff3a..990ff6d416 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs @@ -66,6 +66,14 @@ public partial class UniformUnmanagedMemoryPoolTests return; } +#if NET7_0_OR_GREATER + if (TestEnvironment.OSArchitecture == Architecture.Arm64) + { + // Skip on .NET7 ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + return; + } +#endif + RemoteExecutor.Invoke(RunTest).Dispose(); static void RunTest() From bcf9add4f02c11435a6a5144363296be5b0cd745 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 22:11:14 +1000 Subject: [PATCH 21/47] Update UniformUnmanagedMemoryPoolTests.Trim.cs --- .../Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs index 990ff6d416..3ab3a7162b 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs @@ -2,6 +2,9 @@ // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices; +#endif using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Memory.Internals; From 2b1f6feb0a2f2c0983b832e950be5328000fdb2d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 4 Feb 2023 22:21:57 +1000 Subject: [PATCH 22/47] Remove .NET version constraints from allocator tests --- .../UniformUnmanagedMemoryPoolTests.Trim.cs | 14 +++++--------- .../UniformUnmanagedPoolMemoryAllocatorTests.cs | 8 ++------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs index 3ab3a7162b..17f04795dc 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs @@ -2,9 +2,7 @@ // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; -#if NET7_0_OR_GREATER using System.Runtime.InteropServices; -#endif using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Memory.Internals; @@ -69,13 +67,11 @@ public partial class UniformUnmanagedMemoryPoolTests return; } -#if NET7_0_OR_GREATER - if (TestEnvironment.OSArchitecture == Architecture.Arm64) - { - // Skip on .NET7 ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 - return; - } -#endif + if (TestEnvironment.OSArchitecture == Architecture.Arm64) + { + // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + return; + } RemoteExecutor.Invoke(RunTest).Dispose(); diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index d4284dbd2c..3403e3f17e 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -261,13 +261,11 @@ public class UniformUnmanagedPoolMemoryAllocatorTests return; } -#if NET7_0_OR_GREATER if (TestEnvironment.OSArchitecture == Architecture.Arm64) { - // Skip on .NET7 ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 return; } -#endif if (!TestEnvironment.RunsOnCI) { @@ -331,13 +329,11 @@ public class UniformUnmanagedPoolMemoryAllocatorTests return; } -#if NET7_0_OR_GREATER if (TestEnvironment.OSArchitecture == Architecture.Arm64) { - // Skip on .NET7 ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 return; } -#endif if (!TestEnvironment.RunsOnCI) { From 3fd4fdc0c0225c2cdc4f2f5b1753290b13f7a75e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 4 Feb 2023 20:04:09 +0100 Subject: [PATCH 23/47] Fix wrong casing of file endings from BMP to bmp --- tests/ImageSharp.Tests/TestImages.cs | 16 ++++++++-------- tests/Images/Input/Bmp/{9S.BMP => 9S.bmp} | 0 .../Input/Bmp/{DIAMOND.BMP => DIAMOND.bmp} | 0 .../Input/Bmp/{GMARBLE.BMP => GMARBLE.bmp} | 0 tests/Images/Input/Bmp/{PINES.BMP => PINES.bmp} | 0 .../Images/Input/Bmp/{SKATER.BMP => SKATER.bmp} | 0 tests/Images/Input/Bmp/{SPADE.BMP => SPADE.bmp} | 0 .../Input/Bmp/{SUNFLOW.BMP => SUNFLOW.bmp} | 0 tests/Images/Input/Bmp/{WARPD.BMP => WARPD.bmp} | 0 9 files changed, 8 insertions(+), 8 deletions(-) rename tests/Images/Input/Bmp/{9S.BMP => 9S.bmp} (100%) rename tests/Images/Input/Bmp/{DIAMOND.BMP => DIAMOND.bmp} (100%) rename tests/Images/Input/Bmp/{GMARBLE.BMP => GMARBLE.bmp} (100%) rename tests/Images/Input/Bmp/{PINES.BMP => PINES.bmp} (100%) rename tests/Images/Input/Bmp/{SKATER.BMP => SKATER.bmp} (100%) rename tests/Images/Input/Bmp/{SPADE.BMP => SPADE.bmp} (100%) rename tests/Images/Input/Bmp/{SUNFLOW.BMP => SUNFLOW.bmp} (100%) rename tests/Images/Input/Bmp/{WARPD.BMP => WARPD.bmp} (100%) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c282d81665..7c383d389d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -377,14 +377,14 @@ public static class TestImages public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public const string Os2v2 = "Bmp/pal8os2v2.bmp"; public const string Os2BitmapArray = "Bmp/ba-bm.bmp"; - public const string Os2BitmapArray9s = "Bmp/9S.BMP"; - public const string Os2BitmapArrayDiamond = "Bmp/DIAMOND.BMP"; - public const string Os2BitmapArrayMarble = "Bmp/GMARBLE.BMP"; - public const string Os2BitmapArraySkater = "Bmp/SKATER.BMP"; - public const string Os2BitmapArraySpade = "Bmp/SPADE.BMP"; - public const string Os2BitmapArraySunflower = "Bmp/SUNFLOW.BMP"; - public const string Os2BitmapArrayWarpd = "Bmp/WARPD.BMP"; - public const string Os2BitmapArrayPines = "Bmp/PINES.BMP"; + public const string Os2BitmapArray9s = "Bmp/9S.bmp"; + public const string Os2BitmapArrayDiamond = "Bmp/DIAMOND.bmp"; + public const string Os2BitmapArrayMarble = "Bmp/GMARBLE.bmp"; + public const string Os2BitmapArraySkater = "Bmp/SKATER.bmp"; + public const string Os2BitmapArraySpade = "Bmp/SPADE.bmp"; + public const string Os2BitmapArraySunflower = "Bmp/SUNFLOW.bmp"; + public const string Os2BitmapArrayWarpd = "Bmp/WARPD.bmp"; + public const string Os2BitmapArrayPines = "Bmp/PINES.bmp"; public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp"; public const string Pal8Offset = "Bmp/pal8offs.bmp"; public const string OversizedPalette = "Bmp/pal8oversizepal.bmp"; diff --git a/tests/Images/Input/Bmp/9S.BMP b/tests/Images/Input/Bmp/9S.bmp similarity index 100% rename from tests/Images/Input/Bmp/9S.BMP rename to tests/Images/Input/Bmp/9S.bmp diff --git a/tests/Images/Input/Bmp/DIAMOND.BMP b/tests/Images/Input/Bmp/DIAMOND.bmp similarity index 100% rename from tests/Images/Input/Bmp/DIAMOND.BMP rename to tests/Images/Input/Bmp/DIAMOND.bmp diff --git a/tests/Images/Input/Bmp/GMARBLE.BMP b/tests/Images/Input/Bmp/GMARBLE.bmp similarity index 100% rename from tests/Images/Input/Bmp/GMARBLE.BMP rename to tests/Images/Input/Bmp/GMARBLE.bmp diff --git a/tests/Images/Input/Bmp/PINES.BMP b/tests/Images/Input/Bmp/PINES.bmp similarity index 100% rename from tests/Images/Input/Bmp/PINES.BMP rename to tests/Images/Input/Bmp/PINES.bmp diff --git a/tests/Images/Input/Bmp/SKATER.BMP b/tests/Images/Input/Bmp/SKATER.bmp similarity index 100% rename from tests/Images/Input/Bmp/SKATER.BMP rename to tests/Images/Input/Bmp/SKATER.bmp diff --git a/tests/Images/Input/Bmp/SPADE.BMP b/tests/Images/Input/Bmp/SPADE.bmp similarity index 100% rename from tests/Images/Input/Bmp/SPADE.BMP rename to tests/Images/Input/Bmp/SPADE.bmp diff --git a/tests/Images/Input/Bmp/SUNFLOW.BMP b/tests/Images/Input/Bmp/SUNFLOW.bmp similarity index 100% rename from tests/Images/Input/Bmp/SUNFLOW.BMP rename to tests/Images/Input/Bmp/SUNFLOW.bmp diff --git a/tests/Images/Input/Bmp/WARPD.BMP b/tests/Images/Input/Bmp/WARPD.bmp similarity index 100% rename from tests/Images/Input/Bmp/WARPD.BMP rename to tests/Images/Input/Bmp/WARPD.bmp From dba45c06e71aaf26015dbb6fc527d2adbe8eede0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Feb 2023 12:58:06 +1000 Subject: [PATCH 24/47] Attempt to restrict trigger for ARM to labels --- .github/workflows/build-and-test.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index c033dd133c..dfaaf2f649 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -9,10 +9,13 @@ on: pull_request: branches: - main + types: [ labeled, opened, synchronize, reopened ] jobs: Build: strategy: matrix: + isARM: + - contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') options: - os: ubuntu-latest framework: net7.0 @@ -47,6 +50,14 @@ jobs: sdk: 6.0.x runtime: -x64 codecov: false + - os: buildjet-4vcpu-ubuntu-2204-arm + framework: net6.0 + sdk: 6.0.x + runtime: -x64 + codecov: false + exclude: + - isARM: false + options.os: buildjet-4vcpu-ubuntu-2204-arm runs-on: ${{matrix.options.os}} From 3d39a6c5cfea15eb8bf715bca018ff9a018abbb1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Feb 2023 13:05:00 +1000 Subject: [PATCH 25/47] Try nesting --- .github/workflows/build-and-test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 30e2ef8bc4..c7183b6ee9 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -63,7 +63,8 @@ jobs: codecov: false exclude: - isARM: false - options.os: buildjet-4vcpu-ubuntu-2204-arm + options: + os: buildjet-4vcpu-ubuntu-2204-arm runs-on: ${{matrix.options.os}} From aeca1175d83e0c1bb233b9c5215b9a96d24380f1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Feb 2023 13:07:43 +1000 Subject: [PATCH 26/47] Wrap with braces --- .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 c7183b6ee9..001244a895 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: isARM: - - contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') + - ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }} options: - os: ubuntu-latest framework: net7.0 From b8a76143cc5f0ea070f0b1072c85dd13d4555aa3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Feb 2023 22:58:13 +1000 Subject: [PATCH 27/47] Recreate @brianpopow 's changes --- src/ImageSharp/Compression/Zlib/Adler32.cs | 108 ++++++++++++++++-- src/ImageSharp/Compression/Zlib/Crc32.cs | 95 +++++++++++++++ .../Formats/Png/Filters/AverageFilter.cs | 34 +++++- .../Formats/Png/Filters/PaethFilter.cs | 79 +++++++++++++ .../Formats/Png/Filters/SubFilter.cs | 29 +++++ .../Formats/Png/Filters/UpFilter.cs | 46 +++++++- .../Formats/Png/Crc32Tests.cs | 36 ++++-- .../FeatureTesting/FeatureTestRunner.cs | 31 +++-- .../Tests/FeatureTestRunnerTests.cs | 56 ++++++++- 9 files changed, 476 insertions(+), 38 deletions(-) diff --git a/src/ImageSharp/Compression/Zlib/Adler32.cs b/src/ImageSharp/Compression/Zlib/Adler32.cs index 3885ef5755..1b1a77715d 100644 --- a/src/ImageSharp/Compression/Zlib/Adler32.cs +++ b/src/ImageSharp/Compression/Zlib/Adler32.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; #pragma warning disable IDE0007 // Use implicit type @@ -95,7 +96,7 @@ internal static class Adler32 Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr); Vector128 tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10)); Vector128 zero = Vector128.Zero; - var ones = Vector128.Create((short)1); + Vector128 ones = Vector128.Create((short)1); while (blocks > 0) { @@ -179,13 +180,13 @@ internal static class Adler32 byte* localBufferPtr = bufferPtr; Vector256 zero = Vector256.Zero; - var dot3v = Vector256.Create((short)1); - var dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); + Vector256 dot3v = Vector256.Create((short)1); + Vector256 dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); // Process n blocks of data. At most NMAX data bytes can be // processed before s2 must be reduced modulo BASE. - var vs1 = Vector256.CreateScalar(s1); - var vs2 = Vector256.CreateScalar(s2); + Vector256 vs1 = Vector256.CreateScalar(s1); + Vector256 vs2 = Vector256.CreateScalar(s2); while (length >= 32) { @@ -243,6 +244,100 @@ internal static class Adler32 } } + // Based on: https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + private static unsafe uint CalculateArm(uint adler, ReadOnlySpan buffer) + { + // Split Adler-32 into component sums. + uint s1 = adler & 0xFFFF; + uint s2 = (adler >> 16) & 0xFFFF; + uint length = (uint)buffer.Length; + + // Process the data in blocks. + long blocks = length / BlockSize; + length -= (uint)(blocks * BlockSize); + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + { + byte* localBufferPtr = bufferPtr; + + while (blocks != 0) + { + uint n = NMAX / BlockSize; + if (n > blocks) + { + n = (uint)blocks; + } + + blocks -= n; + + // Process n blocks of data. At most nMax data bytes can be + // processed before s2 must be reduced modulo Base. + Vector128 vs1 = Vector128.Zero; + Vector128 vs2 = vs1.WithElement(3, s1 * n); + Vector128 vColumnSum1 = Vector128.Zero; + Vector128 vColumnSum2 = Vector128.Zero; + Vector128 vColumnSum3 = Vector128.Zero; + Vector128 vColumnSum4 = Vector128.Zero; + + do + { + // Load 32 input bytes. + Vector128 bytes1 = AdvSimd.LoadVector128(localBufferPtr).AsUInt16(); + Vector128 bytes2 = AdvSimd.LoadVector128(localBufferPtr + 0x10).AsUInt16(); + + // Add previous block byte sum to v_s2. + vs2 = AdvSimd.Add(vs2, vs1); + + // Horizontally add the bytes for s1. + vs1 = AdvSimd.AddPairwiseWideningAndAdd( + vs1.AsUInt32(), + AdvSimd.AddPairwiseWideningAndAdd(AdvSimd.AddPairwiseWidening(bytes1.AsByte()).AsUInt16(), bytes2.AsByte())); + + // Vertically add the bytes for s2. + vColumnSum1 = AdvSimd.AddWideningLower(vColumnSum1, bytes1.GetLower().AsByte()); + vColumnSum2 = AdvSimd.AddWideningLower(vColumnSum2, bytes1.GetUpper().AsByte()); + vColumnSum3 = AdvSimd.AddWideningLower(vColumnSum3, bytes2.GetLower().AsByte()); + vColumnSum4 = AdvSimd.AddWideningLower(vColumnSum4, bytes2.GetUpper().AsByte()); + + localBufferPtr += BlockSize; + } + while (--n > 0); + + vs2 = AdvSimd.ShiftLeftLogical(vs2, 5); + + // Multiply-add bytes by [ 32, 31, 30, ... ] for s2. + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum1.GetLower(), Vector64.Create((ushort)32, 31, 30, 29)); + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum1.GetUpper(), Vector64.Create((ushort)28, 27, 26, 25)); + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum2.GetLower(), Vector64.Create((ushort)24, 23, 22, 21)); + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum2.GetUpper(), Vector64.Create((ushort)20, 19, 18, 17)); + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum3.GetLower(), Vector64.Create((ushort)16, 15, 14, 13)); + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum3.GetUpper(), Vector64.Create((ushort)12, 11, 10, 9)); + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum4.GetLower(), Vector64.Create((ushort)8, 7, 6, 5)); + vs2 = AdvSimd.MultiplyWideningLowerAndAdd(vs2, vColumnSum4.GetUpper(), Vector64.Create((ushort)4, 3, 2, 1)); + + // Sum epi32 ints v_s1(s2) and accumulate in s1(s2). + Vector64 sum1 = AdvSimd.AddPairwise(vs1.GetLower(), vs1.GetUpper()); + Vector64 sum2 = AdvSimd.AddPairwise(vs2.GetLower(), vs2.GetUpper()); + Vector64 s1s2 = AdvSimd.AddPairwise(sum1, sum2); + + // Store the results. + s1 += AdvSimd.Extract(s1s2, 0); + s2 += AdvSimd.Extract(s1s2, 1); + + // Reduce. + s1 %= BASE; + s2 %= BASE; + } + + if (length > 0) + { + HandleLeftOver(localBufferPtr, length, ref s1, ref s2); + } + + return s1 | (s2 << 16); + } + } + private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2) { if (length >= 16) @@ -286,7 +381,6 @@ internal static class Adler32 { uint s1 = adler & 0xFFFF; uint s2 = (adler >> 16) & 0xFFFF; - uint k; fixed (byte* bufferPtr = buffer) { @@ -295,7 +389,7 @@ internal static class Adler32 while (length > 0) { - k = length < NMAX ? length : NMAX; + uint k = length < NMAX ? length : NMAX; length -= k; while (k >= 16) diff --git a/src/ImageSharp/Compression/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs index b8665bd43a..c85d58df5b 100644 --- a/src/ImageSharp/Compression/Zlib/Crc32.cs +++ b/src/ImageSharp/Compression/Zlib/Crc32.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; +using ArmCrc32 = System.Runtime.Intrinsics.Arm.Crc32; namespace SixLabors.ImageSharp.Compression.Zlib; @@ -187,6 +188,100 @@ internal static partial class Crc32 } } + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + private static unsafe uint CalculateArm(uint crc, ReadOnlySpan buffer) + { + fixed (byte* bufferPtr = buffer) + { + byte* localBufferPtr = bufferPtr; + int len = buffer.Length; + + while (len > 0 && ((ulong)localBufferPtr & 3) != 0) + { + crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++); + len--; + } + + uint* intBufferPtr = (uint*)localBufferPtr; + + while (len >= 8 * sizeof(uint)) + { + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + len -= 8 * sizeof(uint); + } + + while (len >= sizeof(uint)) + { + crc = ArmCrc32.ComputeCrc32(crc, *intBufferPtr++); + len -= sizeof(uint); + } + + localBufferPtr = (byte*)intBufferPtr; + + while (len > 0) + { + crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++); + len--; + } + + return crc; + } + } + + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + private static unsafe uint CalculateArm64(uint crc, ReadOnlySpan buffer) + { + fixed (byte* bufferPtr = buffer) + { + byte* localBufferPtr = bufferPtr; + int len = buffer.Length; + + while (len > 0 && ((ulong)localBufferPtr & 7) != 0) + { + crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++); + len--; + } + + ulong* longBufferPtr = (ulong*)localBufferPtr; + + while (len >= 8 * sizeof(ulong)) + { + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + len -= 8 * sizeof(ulong); + } + + while (len >= sizeof(ulong)) + { + crc = ArmCrc32.Arm64.ComputeCrc32(crc, *longBufferPtr++); + len -= sizeof(ulong); + } + + localBufferPtr = (byte*)longBufferPtr; + + while (len > 0) + { + crc = ArmCrc32.ComputeCrc32(crc, *localBufferPtr++); + len--; + } + + return crc; + } + } + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] private static uint CalculateScalar(uint crc, ReadOnlySpan buffer) { diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 7a6ea13ca7..34e05d7796 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Formats.Png.Filters; @@ -35,6 +36,10 @@ internal static class AverageFilter { DecodeSse2(scanline, previousScanline); } + else if (AdvSimd.IsSupported && bytesPerPixel is 4) + { + DecodeArm(scanline, previousScanline); + } else { DecodeScalar(scanline, previousScanline, bytesPerPixel); @@ -48,7 +53,7 @@ internal static class AverageFilter ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); Vector128 d = Vector128.Zero; - var ones = Vector128.Create((byte)1); + Vector128 ones = Vector128.Create((byte)1); int rb = scanline.Length; nint offset = 1; @@ -75,6 +80,33 @@ internal static class AverageFilter } } + public static void DecodeArm(Span scanline, Span previousScanline) + { + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + + Vector64 d = Vector64.Zero; + + int rb = scanline.Length; + int offset = 1; + const int bytesPerBatch = 4; + while (rb >= bytesPerBatch) + { + ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); + Vector64 a = d; + Vector64 b = Vector64.CreateScalar(Unsafe.As(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte(); + d = Vector64.CreateScalar(Unsafe.As(ref scanRef)).AsByte(); + + Vector64 avg = AdvSimd.FusedAddHalving(a, b); + d = AdvSimd.Add(d, avg); + + Unsafe.As(ref scanRef) = d.AsInt32().ToScalar(); + + rb -= bytesPerBatch; + offset += bytesPerBatch; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel) { diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index aee68487dd..8998d6bc0f 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Formats.Png.Filters; @@ -38,6 +39,10 @@ internal static class PaethFilter { DecodeSse41(scanline, previousScanline); } + else if (AdvSimd.Arm64.IsSupported && bytesPerPixel is 4) + { + DecodeArm(scanline, previousScanline); + } else { DecodeScalar(scanline, previousScanline, bytesPerPixel); @@ -99,6 +104,80 @@ internal static class PaethFilter } } + public static void DecodeArm(Span scanline, Span previousScanline) + { + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + + Vector128 b = Vector128.Zero; + Vector128 d = Vector128.Zero; + + int rb = scanline.Length; + nint offset = 1; + const int bytesPerBatch = 4; + while (rb >= bytesPerBatch) + { + ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); + Vector128 c = b; + Vector128 a = d; + b = AdvSimd.Arm64.ZipLow( + Vector128.CreateScalar(Unsafe.As(ref Unsafe.Add(ref prevBaseRef, offset))).AsByte(), + Vector128.Zero).AsByte(); + d = AdvSimd.Arm64.ZipLow( + Vector128.CreateScalar(Unsafe.As(ref scanRef)).AsByte(), + Vector128.Zero).AsByte(); + + // (p-a) == (a+b-c - a) == (b-c) + Vector128 pa = AdvSimd.Subtract(b.AsInt16(), c.AsInt16()); + + // (p-b) == (a+b-c - b) == (a-c) + Vector128 pb = AdvSimd.Subtract(a.AsInt16(), c.AsInt16()); + + // (p-c) == (a+b-c - c) == (a+b-c-c) == (b-c)+(a-c) + Vector128 pc = AdvSimd.Add(pa.AsInt16(), pb.AsInt16()); + + pa = AdvSimd.Abs(pa.AsInt16()).AsInt16(); /* |p-a| */ + pb = AdvSimd.Abs(pb.AsInt16()).AsInt16(); /* |p-b| */ + pc = AdvSimd.Abs(pc.AsInt16()).AsInt16(); /* |p-c| */ + + Vector128 smallest = AdvSimd.Min(pc, AdvSimd.Min(pa, pb)); + + // Paeth breaks ties favoring a over b over c. + Vector128 mask = BlendVariable(c, b, AdvSimd.CompareEqual(smallest, pb).AsByte()); + Vector128 nearest = BlendVariable(mask, a, AdvSimd.CompareEqual(smallest, pa).AsByte()); + + d = AdvSimd.Add(d, nearest); + + Vector64 e = AdvSimd.ExtractNarrowingSaturateUnsignedLower(d.AsInt16()); + + Unsafe.As(ref scanRef) = Vector128.Create(e, e).AsInt32().ToScalar(); + + rb -= bytesPerBatch; + offset += bytesPerBatch; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 BlendVariable(Vector128 a, Vector128 b, Vector128 c) + { + // Equivalent of Sse41.BlendVariable: + // Blend packed 8-bit integers from a and b using mask, and store the results in + // dst. + // + // FOR j := 0 to 15 + // i := j*8 + // IF mask[i+7] + // dst[i+7:i] := b[i+7:i] + // ELSE + // dst[i+7:i] := a[i+7:i] + // FI + // ENDFOR + // + // Use a signed shift right to create a mask with the sign bit. + Vector128 mask = AdvSimd.ShiftRightArithmetic(c.AsInt16(), 7); + return AdvSimd.BitwiseSelect(mask, b.AsInt16(), a.AsInt16()).AsByte(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel) { diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index dcf55237d3..0f4aa3fcff 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Formats.Png.Filters; @@ -29,6 +30,10 @@ internal static class SubFilter { DecodeSse2(scanline); } + else if (AdvSimd.IsSupported && bytesPerPixel is 4) + { + DecodeArm(scanline); + } else { DecodeScalar(scanline, bytesPerPixel); @@ -58,6 +63,30 @@ internal static class SubFilter } } + public static void DecodeArm(Span scanline) + { + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + + Vector64 d = Vector64.Zero; + + int rb = scanline.Length; + int offset = 1; + const int bytesPerBatch = 4; + while (rb >= bytesPerBatch) + { + ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); + Vector64 a = d; + d = Vector64.CreateScalar(Unsafe.As(ref scanRef)).AsByte(); + + d = AdvSimd.Add(d, a); + + Unsafe.As(ref scanRef) = d.AsInt32().ToScalar(); + + rb -= bytesPerBatch; + offset += bytesPerBatch; + } + } + private static void DecodeScalar(Span scanline, int bytesPerPixel) { ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index 833563655c..f90b129b62 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; namespace SixLabors.ImageSharp.Formats.Png.Filters; @@ -34,6 +35,10 @@ internal static class UpFilter { DecodeSse2(scanline, previousScanline); } + else if (AdvSimd.IsSupported) + { + DecodeArm(scanline, previousScanline); + } else { DecodeScalar(scanline, previousScanline); @@ -51,11 +56,10 @@ internal static class UpFilter while (rb >= Vector256.Count) { ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); - Vector256 current = Unsafe.As>(ref scanRef); + Vector256 prior = Unsafe.As>(ref scanRef); Vector256 up = Unsafe.As>(ref Unsafe.Add(ref prevBaseRef, offset)); - Vector256 sum = Avx2.Add(up, current); - Unsafe.As>(ref scanRef) = sum; + Unsafe.As>(ref scanRef) = Avx2.Add(up, prior); offset += Vector256.Count; rb -= Vector256.Count; @@ -82,11 +86,10 @@ internal static class UpFilter while (rb >= Vector128.Count) { ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); - Vector128 current = Unsafe.As>(ref scanRef); + Vector128 prior = Unsafe.As>(ref scanRef); Vector128 up = Unsafe.As>(ref Unsafe.Add(ref prevBaseRef, offset)); - Vector128 sum = Sse2.Add(up, current); - Unsafe.As>(ref scanRef) = sum; + Unsafe.As>(ref scanRef) = Sse2.Add(up, prior); offset += Vector128.Count; rb -= Vector128.Count; @@ -102,6 +105,37 @@ internal static class UpFilter } } + private static void DecodeArm(Span scanline, Span previousScanline) + { + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + + // Up(x) + Prior(x) + int rb = scanline.Length; + nint offset = 1; + const int bytesPerBatch = 16; + while (rb >= bytesPerBatch) + { + ref byte scanRef = ref Unsafe.Add(ref scanBaseRef, offset); + Vector128 prior = Unsafe.As>(ref scanRef); + Vector128 up = Unsafe.As>(ref Unsafe.Add(ref prevBaseRef, offset)); + + Unsafe.As>(ref scanRef) = AdvSimd.Add(prior, up); + + offset += bytesPerBatch; + rb -= bytesPerBatch; + } + + // Handle left over. + for (nint i = offset; i < scanline.Length; i++) + { + ref byte scan = ref Unsafe.Add(ref scanBaseRef, offset); + byte above = Unsafe.Add(ref prevBaseRef, offset); + scan = (byte)(scan + above); + offset++; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeScalar(Span scanline, Span previousScanline) { diff --git a/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs b/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs index 0dea05c531..ff91590f95 100644 --- a/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/Crc32Tests.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Tests.TestUtilities; using SharpCrc32 = ICSharpCode.SharpZipLib.Checksum.Crc32; namespace SixLabors.ImageSharp.Tests.Formats.Png; @@ -13,10 +14,7 @@ public class Crc32Tests [InlineData(0)] [InlineData(1)] [InlineData(2)] - public void ReturnsCorrectWhenEmpty(uint input) - { - Assert.Equal(input, Crc32.Calculate(input, default)); - } + public void CalculateCrc_ReturnsCorrectResultWhenEmpty(uint input) => Assert.Equal(input, Crc32.Calculate(input, default)); [Theory] [InlineData(0)] @@ -26,23 +24,43 @@ public class Crc32Tests [InlineData(1024 + 15)] [InlineData(2034)] [InlineData(4096)] - public void MatchesReference(int length) + public void CalculateCrc_MatchesReference(int length) => CalculateCrcAndCompareToReference(length); + + private static void CalculateCrcAndCompareToReference(int length) { - var data = GetBuffer(length); - var crc = new SharpCrc32(); + // arrange + byte[] data = GetBuffer(length); + SharpCrc32 crc = new(); crc.Update(data); - long expected = crc.Value; + + // act long actual = Crc32.Calculate(data); + // assert Assert.Equal(expected, actual); } private static byte[] GetBuffer(int length) { - var data = new byte[length]; + byte[] data = new byte[length]; new Random(1).NextBytes(data); return data; } + + [Fact] + public void RunCalculateCrcTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCalculateCrcTest, HwIntrinsics.AllowAll); + + [Fact] + public void RunCalculateCrcTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCalculateCrcTest, HwIntrinsics.DisableHWIntrinsic); + + private static void RunCalculateCrcTest() + { + int[] testData = { 0, 8, 215, 1024, 1024 + 15, 2034, 4096 }; + for (int i = 0; i < testData.Length; i++) + { + CalculateCrcAndCompareToReference(testData[i]); + } + } } diff --git a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs index ad5aa4769f..1bb64d99d6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs +++ b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities; /// public static class FeatureTestRunner { - private static readonly char[] SplitChars = new[] { ',', ' ' }; + private static readonly char[] SplitChars = { ',', ' ' }; /// /// Allows the deserialization of parameters passed to the feature test. @@ -35,6 +35,7 @@ public static class FeatureTestRunner /// Allows the deserialization of types implementing /// passed to the feature test. /// + /// The type of object to deserialize. /// The string value to deserialize. /// The value. public static T Deserialize(string value) @@ -58,7 +59,7 @@ public static class FeatureTestRunner foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) { - var processStartInfo = new ProcessStartInfo(); + ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; @@ -99,7 +100,7 @@ public static class FeatureTestRunner foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) { - var processStartInfo = new ProcessStartInfo(); + ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; @@ -142,7 +143,7 @@ public static class FeatureTestRunner foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) { - var processStartInfo = new ProcessStartInfo(); + ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; @@ -185,7 +186,7 @@ public static class FeatureTestRunner foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) { - var processStartInfo = new ProcessStartInfo(); + ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; @@ -232,7 +233,7 @@ public static class FeatureTestRunner foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) { - var processStartInfo = new ProcessStartInfo(); + ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; @@ -276,7 +277,7 @@ public static class FeatureTestRunner foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) { - var processStartInfo = new ProcessStartInfo(); + ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; @@ -321,7 +322,7 @@ public static class FeatureTestRunner foreach (KeyValuePair intrinsic in intrinsics.ToFeatureKeyValueCollection()) { - var processStartInfo = new ProcessStartInfo(); + ProcessStartInfo processStartInfo = new(); if (intrinsic.Key != HwIntrinsics.AllowAll) { processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0"; @@ -347,11 +348,11 @@ public static class FeatureTestRunner internal static Dictionary ToFeatureKeyValueCollection(this HwIntrinsics intrinsics) { - // Loop through and translate the given values into COMPlus equivaluents - var features = new Dictionary(); + // Loop through and translate the given values into COMPlus equivalents + Dictionary features = new(); foreach (string intrinsic in intrinsics.ToString("G").Split(SplitChars, StringSplitOptions.RemoveEmptyEntries)) { - var key = (HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic); + HwIntrinsics key = (HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic); switch (intrinsic) { case nameof(HwIntrinsics.AllowAll): @@ -400,5 +401,11 @@ public enum HwIntrinsics DisableBMI1 = 1 << 13, DisableBMI2 = 1 << 14, DisableLZCNT = 1 << 15, - AllowAll = 1 << 16 + DisableArm64AdvSimd = 1 << 16, + DisableArm64Crc32 = 1 << 17, + DisableArm64Dp = 1 << 18, + DisableArm64Aes = 1 << 19, + DisableArm64Sha1 = 1 << 20, + DisableArm64Sha256 = 1 << 21, + AllowAll = 1 << 22 } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index 6ce07e7665..34337600eb 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -2,8 +2,10 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; using Xunit.Abstractions; +using Aes = System.Runtime.Intrinsics.X86.Aes; namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests; @@ -12,9 +14,9 @@ public class FeatureTestRunnerTests public static TheoryData Intrinsics => new() { - { HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, new string[] { "EnableAES", "AllowAll" } }, - { HwIntrinsics.DisableHWIntrinsic, new string[] { "EnableHWIntrinsic" } }, - { HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, new string[] { "EnableSSE42", "EnableAVX" } } + { HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, new[] { "EnableAES", "AllowAll" } }, + { HwIntrinsics.DisableHWIntrinsic, new[] { "EnableHWIntrinsic" } }, + { HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, new[] { "EnableSSE42", "EnableAVX" } } }; [Theory] @@ -101,6 +103,12 @@ public class FeatureTestRunnerTests Assert.False(Bmi1.IsSupported); Assert.False(Bmi2.IsSupported); Assert.False(Lzcnt.IsSupported); + Assert.False(AdvSimd.IsSupported); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + Assert.False(Crc32.IsSupported); + Assert.False(Dp.IsSupported); + Assert.False(Sha1.IsSupported); + Assert.False(Sha256.IsSupported); break; case HwIntrinsics.DisableSSE: Assert.False(Sse.IsSupported); @@ -147,6 +155,24 @@ public class FeatureTestRunnerTests case HwIntrinsics.DisableLZCNT: Assert.False(Lzcnt.IsSupported); break; + case HwIntrinsics.DisableArm64AdvSimd: + Assert.False(AdvSimd.IsSupported); + break; + case HwIntrinsics.DisableArm64Aes: + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + break; + case HwIntrinsics.DisableArm64Crc32: + Assert.False(Crc32.IsSupported); + break; + case HwIntrinsics.DisableArm64Dp: + Assert.False(Dp.IsSupported); + break; + case HwIntrinsics.DisableArm64Sha1: + Assert.False(Sha1.IsSupported); + break; + case HwIntrinsics.DisableArm64Sha256: + Assert.False(Sha256.IsSupported); + break; } } @@ -198,6 +224,12 @@ public class FeatureTestRunnerTests Assert.False(Bmi1.IsSupported); Assert.False(Bmi2.IsSupported); Assert.False(Lzcnt.IsSupported); + Assert.False(AdvSimd.IsSupported); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + Assert.False(Crc32.IsSupported); + Assert.False(Dp.IsSupported); + Assert.False(Sha1.IsSupported); + Assert.False(Sha256.IsSupported); break; case HwIntrinsics.DisableSSE: Assert.False(Sse.IsSupported); @@ -244,6 +276,24 @@ public class FeatureTestRunnerTests case HwIntrinsics.DisableLZCNT: Assert.False(Lzcnt.IsSupported); break; + case HwIntrinsics.DisableArm64AdvSimd: + Assert.False(AdvSimd.IsSupported); + break; + case HwIntrinsics.DisableArm64Aes: + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + break; + case HwIntrinsics.DisableArm64Crc32: + Assert.False(Crc32.IsSupported); + break; + case HwIntrinsics.DisableArm64Dp: + Assert.False(Dp.IsSupported); + break; + case HwIntrinsics.DisableArm64Sha1: + Assert.False(Sha1.IsSupported); + break; + case HwIntrinsics.DisableArm64Sha256: + Assert.False(Sha256.IsSupported); + break; } } From 3182f935dc84b4ebfce2b506ab0d5eb83ca8aa5a Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 15:25:49 +0100 Subject: [PATCH 28/47] Remove nullable disable from Formats.BMP * Removed nullable disable * refactored stream usage. It is now always passed as a parameter and not stored a a instance variable --- src/ImageSharp/Color/Color.cs | 3 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 179 ++++++++++-------- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 5 +- .../PaletteDitherProcessor{TPixel}.cs | 2 +- .../Processors/Quantization/IQuantizer.cs | 4 +- .../Quantization/OctreeQuantizer.cs | 4 +- .../Quantization/OctreeQuantizer{TPixel}.cs | 2 +- .../Quantization/PaletteQuantizer.cs | 6 +- .../Quantization/PaletteQuantizer{TPixel}.cs | 2 +- .../Processors/Quantization/WuQuantizer.cs | 4 +- .../Quantization/WuQuantizer{TPixel}.cs | 2 +- 11 files changed, 112 insertions(+), 101 deletions(-) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 99a271bcf7..590a3ed311 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -286,12 +286,11 @@ public readonly partial struct Color : IEquatable /// Bulk converts a span of to a span of a specified type. /// /// The pixel type to convert to. - /// The configuration. /// The source color span. /// The destination pixel span. [MethodImpl(InliningOptions.ShortMethod)] #pragma warning disable RCS1163 // Unused parameter. - public static void ToPixel(Configuration configuration, ReadOnlySpan source, Span destination) + public static void ToPixel(ReadOnlySpan source, Span destination) #pragma warning restore RCS1163 // Unused parameter. where TPixel : unmanaged, IPixel { diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index fd0497d9ac..cb1c7a2511 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1,9 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Buffers.Binary; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Common.Helpers; @@ -58,20 +58,15 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// private const int RleDelta = 0x02; - /// - /// The stream to decode from. - /// - private BufferedReadStream stream; - /// /// The metadata. /// - private ImageMetadata metadata; + private ImageMetadata? metadata; /// /// The bitmap specific metadata. /// - private BmpMetadata bmpMetadata; + private BmpMetadata? bmpMetadata; /// /// The file header containing general information. @@ -126,7 +121,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { - Image image = null; + Image? image = null; try { int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); @@ -142,24 +137,25 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3) { - this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); } else { - this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); } } else if (this.infoHeader.BitsPerPixel == 24) { - this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); } else if (this.infoHeader.BitsPerPixel == 16) { - this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + this.ReadRgb16(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); } else if (this.infoHeader.BitsPerPixel <= 8) { this.ReadRgbPalette( + stream, pixels, palette, this.infoHeader.Width, @@ -172,19 +168,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals break; case BmpCompression.RLE24: - this.ReadRle24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + this.ReadRle24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); break; case BmpCompression.RLE8: case BmpCompression.RLE4: - this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); + this.ReadRle(stream, this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); break; case BmpCompression.BitFields: case BmpCompression.BI_ALPHABITFIELDS: - this.ReadBitFields(pixels, inverted); + this.ReadBitFields(stream, pixels, inverted); break; @@ -250,14 +246,16 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// which will be used to determine which bits belong to that channel. /// /// The pixel format. + /// The containing image data. /// The output pixel buffer containing the decoded image. /// Whether the bitmap is inverted. - private void ReadBitFields(Buffer2D pixels, bool inverted) + private void ReadBitFields(BufferedReadStream stream, Buffer2D pixels, bool inverted) where TPixel : unmanaged, IPixel { if (this.infoHeader.BitsPerPixel == 16) { this.ReadRgb16( + stream, pixels, this.infoHeader.Width, this.infoHeader.Height, @@ -269,6 +267,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals else { this.ReadRgb32BitFields( + stream, pixels, this.infoHeader.Width, this.infoHeader.Height, @@ -282,17 +281,17 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data. - /// Compressed RLE8 stream is uncompressed by - /// Compressed RLE4 stream is uncompressed by + /// Compressed RLE4 stream is uncompressed by /// /// The pixel format. + /// The containing image data. /// The compression type. Either RLE4 or RLE8. /// The to assign the palette to. /// The containing the colors. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRle(BmpCompression compression, Buffer2D pixels, byte[] colors, int width, int height, bool inverted) + private void ReadRle(BufferedReadStream stream, BmpCompression compression, Buffer2D pixels, byte[] colors, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -305,11 +304,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals Span bufferSpan = buffer.Memory.Span; if (compression is BmpCompression.RLE8) { - this.UncompressRle8(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); + this.UncompressRle8(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); } else { - this.UncompressRle4(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); + this.UncompressRle4(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); } for (int y = 0; y < height; y++) @@ -368,11 +367,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Looks up color values and builds the image from de-compressed RLE24. /// /// The pixel format. + /// The containing image data. /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRle24(Buffer2D pixels, int width, int height, bool inverted) + private void ReadRle24(BufferedReadStream stream, Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -384,7 +384,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals Span undefinedPixelsSpan = undefinedPixels.Memory.Span; Span bufferSpan = buffer.GetSpan(); - this.UncompressRle24(width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); + this.UncompressRle24(stream, width, bufferSpan, undefinedPixelsSpan, rowsWithUndefinedPixelsSpan); for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); @@ -446,18 +446,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals ///
If first byte is 0, the second byte may have special meaning. ///
Otherwise, the first byte is the length of the run and second byte contains two color indexes. /// + /// The containing image data. /// The width of the bitmap. /// Buffer for uncompressed data. /// Keeps track over skipped and therefore undefined pixels. /// Keeps track of rows, which have undefined pixels. - private void UncompressRle4(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) + private void UncompressRle4(BufferedReadStream stream, int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { Span cmd = stackalloc byte[2]; int count = 0; while (count < buffer.Length) { - if (this.stream.Read(cmd, 0, cmd.Length) != 2) + if (stream.Read(cmd, 0, cmd.Length) != 2) { BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap."); } @@ -478,8 +479,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals break; case RleDelta: - int dx = this.stream.ReadByte(); - int dy = this.stream.ReadByte(); + int dx = stream.ReadByte(); + int dy = stream.ReadByte(); count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); break; @@ -492,7 +493,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals byte[] run = new byte[bytesToRead]; - this.stream.Read(run, 0, run.Length); + stream.Read(run, 0, run.Length); int idx = 0; for (int i = 0; i < max; i++) @@ -512,7 +513,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // Absolute mode data is aligned to two-byte word-boundary. int padding = bytesToRead & 1; - this.stream.Skip(padding); + stream.Skip(padding); break; } @@ -551,18 +552,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals ///
If first byte is 0, the second byte may have special meaning. ///
Otherwise, the first byte is the length of the run and second byte is the color for the run. /// + /// The containing image data. /// The width of the bitmap. /// Buffer for uncompressed data. /// Keeps track of skipped and therefore undefined pixels. /// Keeps track of rows, which have undefined pixels. - private void UncompressRle8(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) + private void UncompressRle8(BufferedReadStream stream, int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { Span cmd = stackalloc byte[2]; int count = 0; while (count < buffer.Length) { - if (this.stream.Read(cmd, 0, cmd.Length) != 2) + if (stream.Read(cmd, 0, cmd.Length) != 2) { BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap."); } @@ -583,8 +585,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals break; case RleDelta: - int dx = this.stream.ReadByte(); - int dy = this.stream.ReadByte(); + int dx = stream.ReadByte(); + int dy = stream.ReadByte(); count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); break; @@ -596,7 +598,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals byte[] run = new byte[length]; - this.stream.Read(run, 0, run.Length); + stream.Read(run, 0, run.Length); run.AsSpan().CopyTo(buffer[count..]); @@ -605,7 +607,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // Absolute mode data is aligned to two-byte word-boundary. int padding = length & 1; - this.stream.Skip(padding); + stream.Skip(padding); break; } @@ -630,18 +632,19 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals ///
If first byte is 0, the second byte may have special meaning. ///
Otherwise, the first byte is the length of the run and following three bytes are the color for the run. /// + /// The containing image data. /// The width of the bitmap. /// Buffer for uncompressed data. /// Keeps track of skipped and therefore undefined pixels. /// Keeps track of rows, which have undefined pixels. - private void UncompressRle24(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) + private void UncompressRle24(BufferedReadStream stream, int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { Span cmd = stackalloc byte[2]; int uncompressedPixels = 0; while (uncompressedPixels < buffer.Length) { - if (this.stream.Read(cmd, 0, cmd.Length) != 2) + if (stream.Read(cmd, 0, cmd.Length) != 2) { BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap."); } @@ -662,8 +665,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals break; case RleDelta: - int dx = this.stream.ReadByte(); - int dy = this.stream.ReadByte(); + int dx = stream.ReadByte(); + int dy = stream.ReadByte(); uncompressedPixels += RleSkipDelta(uncompressedPixels, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); break; @@ -675,7 +678,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals byte[] run = new byte[length * 3]; - this.stream.Read(run, 0, run.Length); + stream.Read(run, 0, run.Length); run.AsSpan().CopyTo(buffer[(uncompressedPixels * 3)..]); @@ -684,7 +687,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // Absolute mode data is aligned to two-byte word-boundary. int padding = run.Length & 1; - this.stream.Skip(padding); + stream.Skip(padding); break; } @@ -693,8 +696,8 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { int max = uncompressedPixels + cmd[0]; byte blueIdx = cmd[1]; - byte greenIdx = (byte)this.stream.ReadByte(); - byte redIdx = (byte)this.stream.ReadByte(); + byte greenIdx = (byte)stream.ReadByte(); + byte redIdx = (byte)stream.ReadByte(); int bufferIdx = uncompressedPixels * 3; for (; uncompressedPixels < max; uncompressedPixels++) @@ -800,6 +803,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Reads the color palette from the stream. /// /// The pixel format. + /// The containing image data. /// The to assign the palette to. /// The containing the colors. /// The width of the bitmap. @@ -808,7 +812,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps /// the bytes per color palette entry's can be 3 bytes instead of 4. /// Whether the bitmap is inverted. - private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) + private void ReadRgbPalette(BufferedReadStream stream, Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) where TPixel : unmanaged, IPixel { // Pixels per byte (bits per pixel). @@ -833,7 +837,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); - if (this.stream.Read(rowSpan) == 0) + if (stream.Read(rowSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -861,6 +865,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Reads the 16 bit color palette from the stream. /// /// The pixel format. + /// The containing image data. /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. @@ -868,7 +873,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// The bitmask for the red channel. /// The bitmask for the green channel. /// The bitmask for the blue channel. - private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask) + private void ReadRgb16(BufferedReadStream stream, Buffer2D pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask) where TPixel : unmanaged, IPixel { int padding = CalculatePadding(width, 2); @@ -889,7 +894,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals for (int y = 0; y < height; y++) { - if (this.stream.Read(bufferSpan) == 0) + if (stream.Read(bufferSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -935,11 +940,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Reads the 24 bit color palette from the stream. /// /// The pixel format. + /// The containing image data. /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb24(Buffer2D pixels, int width, int height, bool inverted) + private void ReadRgb24(BufferedReadStream stream, Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { int padding = CalculatePadding(width, 3); @@ -948,7 +954,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals for (int y = 0; y < height; y++) { - if (this.stream.Read(rowSpan) == 0) + if (stream.Read(rowSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -967,11 +973,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Reads the 32 bit color palette from the stream. /// /// The pixel format. + /// The containing image data. /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb32Fast(Buffer2D pixels, int width, int height, bool inverted) + private void ReadRgb32Fast(BufferedReadStream stream, Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { int padding = CalculatePadding(width, 4); @@ -980,7 +987,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals for (int y = 0; y < height; y++) { - if (this.stream.Read(rowSpan) == 0) + if (stream.Read(rowSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -1000,11 +1007,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format. /// /// The pixel format. + /// The containing image data. /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb32Slow(Buffer2D pixels, int width, int height, bool inverted) + private void ReadRgb32Slow(BufferedReadStream stream, Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { int padding = CalculatePadding(width, 4); @@ -1012,7 +1020,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals using IMemoryOwner bgraRow = this.memoryAllocator.Allocate(width); Span rowSpan = row.GetSpan(); Span bgraRowSpan = bgraRow.GetSpan(); - long currentPosition = this.stream.Position; + long currentPosition = stream.Position; bool hasAlpha = false; // Loop though the rows checking each pixel. We start by assuming it's @@ -1020,7 +1028,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // actually a BGRA image, and change tactics accordingly. for (int y = 0; y < height; y++) { - if (this.stream.Read(rowSpan) == 0) + if (stream.Read(rowSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -1049,14 +1057,14 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } // Reset our stream for a second pass. - this.stream.Position = currentPosition; + stream.Position = currentPosition; // Process the pixels in bulk taking the raw alpha component value. if (hasAlpha) { for (int y = 0; y < height; y++) { - if (this.stream.Read(rowSpan) == 0) + if (stream.Read(rowSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -1077,7 +1085,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // Slow path. We need to set each alpha component value to fully opaque. for (int y = 0; y < height; y++) { - if (this.stream.Read(rowSpan) == 0) + if (stream.Read(rowSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -1105,6 +1113,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// Decode an 32 Bit Bitmap containing a bitmask for each color channel. /// /// The pixel format. + /// The containing image data. /// The output pixel buffer containing the decoded image. /// The width of the image. /// The height of the image. @@ -1113,7 +1122,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// The bitmask for the green channel. /// The bitmask for the blue channel. /// The bitmask for the alpha channel. - private void ReadRgb32BitFields(Buffer2D pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask) + private void ReadRgb32BitFields(BufferedReadStream stream, Buffer2D pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -1142,7 +1151,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals for (int y = 0; y < height; y++) { - if (this.stream.Read(bufferSpan) == 0) + if (stream.Read(bufferSpan) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); } @@ -1228,10 +1237,13 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// /// Reads the from the stream. /// - private void ReadInfoHeader() + /// The containing image data. + [MemberNotNull(nameof(metadata))] + [MemberNotNull(nameof(bmpMetadata))] + private void ReadInfoHeader(BufferedReadStream stream) { Span buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize]; - long infoHeaderStart = this.stream.Position; + long infoHeaderStart = stream.Position; // Resolution is stored in PPM. this.metadata = new ImageMetadata @@ -1240,7 +1252,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals }; // Read the header size. - this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); + stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); if (headerSize is < BmpInfoHeader.CoreSize or > BmpInfoHeader.MaxHeaderSize) @@ -1249,7 +1261,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } // Read the rest of the header. - this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); + stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2; if (headerSize == BmpInfoHeader.CoreSize) @@ -1275,7 +1287,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals if (this.infoHeader.Compression == BmpCompression.BitFields) { byte[] bitfieldsBuffer = new byte[12]; - this.stream.Read(bitfieldsBuffer, 0, 12); + stream.Read(bitfieldsBuffer, 0, 12); Span data = bitfieldsBuffer.AsSpan(); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); @@ -1284,7 +1296,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS) { byte[] bitfieldsBuffer = new byte[16]; - this.stream.Read(bitfieldsBuffer, 0, 16); + stream.Read(bitfieldsBuffer, 0, 16); Span data = bitfieldsBuffer.AsSpan(); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); @@ -1324,12 +1336,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals if (this.infoHeader.ProfileData != 0 && this.infoHeader.ProfileSize != 0) { // Read color profile. - long streamPosition = this.stream.Position; + long streamPosition = stream.Position; byte[] iccProfileData = new byte[this.infoHeader.ProfileSize]; - this.stream.Position = infoHeaderStart + this.infoHeader.ProfileData; - this.stream.Read(iccProfileData); + stream.Position = infoHeaderStart + this.infoHeader.ProfileData; + stream.Read(iccProfileData); this.metadata.IccProfile = new IccProfile(iccProfileData); - this.stream.Position = streamPosition; + stream.Position = streamPosition; } } else @@ -1358,10 +1370,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// /// Reads the from the stream. /// - private void ReadFileHeader() + /// The containing image data. + private void ReadFileHeader(BufferedReadStream stream) { Span buffer = stackalloc byte[BmpFileHeader.Size]; - this.stream.Read(buffer, 0, BmpFileHeader.Size); + stream.Read(buffer, 0, BmpFileHeader.Size); short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer); switch (fileTypeMarker) @@ -1375,7 +1388,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // Because we only decode the first bitmap in the array, the array header will be ignored. // The bitmap file header of the first image follows the array header. - this.stream.Read(buffer, 0, BmpFileHeader.Size); + stream.Read(buffer, 0, BmpFileHeader.Size); this.fileHeader = BmpFileHeader.Parse(buffer); if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) { @@ -1398,12 +1411,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// The color palette. /// Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps /// the bytes per color palette entry's can be 3 bytes instead of 4. + [MemberNotNull(nameof(metadata))] + [MemberNotNull(nameof(bmpMetadata))] private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette) { - this.stream = stream; - - this.ReadFileHeader(); - this.ReadInfoHeader(); + this.ReadFileHeader(stream); + this.ReadInfoHeader(stream); // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 // If the height is negative, then this is a Windows bitmap whose origin @@ -1451,13 +1464,13 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry; } - palette = null; + palette = Array.Empty(); if (colorMapSizeBytes > 0) { // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. // Make sure, that we will not read pass the bitmap offset (starting position of image data). - if ((this.stream.Position + colorMapSizeBytes) > this.fileHeader.Offset) + if ((stream.Position + colorMapSizeBytes) > this.fileHeader.Offset) { BmpThrowHelper.ThrowInvalidImageContentException( $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset."); @@ -1465,21 +1478,21 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals palette = new byte[colorMapSizeBytes]; - if (this.stream.Read(palette, 0, colorMapSizeBytes) == 0) + if (stream.Read(palette, 0, colorMapSizeBytes) == 0) { BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for the palette!"); } } - int skipAmount = this.fileHeader.Offset - (int)this.stream.Position; - if ((skipAmount + (int)this.stream.Position) > this.stream.Length) + int skipAmount = this.fileHeader.Offset - (int)stream.Position; + if ((skipAmount + (int)stream.Position) > stream.Length) { BmpThrowHelper.ThrowInvalidImageContentException("Invalid file header offset found. Offset is greater than the stream length."); } if (skipAmount > 0) { - this.stream.Skip(skipAmount); + stream.Skip(skipAmount); } return bytesPerColorMapEntry; diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 9f1e856393..0f432fbda9 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Buffers.Binary; @@ -72,7 +71,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// /// The global configuration. /// - private Configuration configuration; + private Configuration? configuration; /// /// The color depth, in number of bits per pixel. @@ -142,7 +141,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals _ => 0 }; - byte[] iccProfileData = null; + byte[] iccProfileData = Array.Empty(); int iccProfileSize = 0; if (metadata.IccProfile != null) { diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index abf283e6c1..982cc7d46c 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -35,7 +35,7 @@ internal sealed class PaletteDitherProcessor : ImageProcessor ReadOnlySpan sourcePalette = definition.Palette.Span; this.paletteOwner = this.Configuration.MemoryAllocator.Allocate(sourcePalette.Length); - Color.ToPixel(this.Configuration, sourcePalette, this.paletteOwner.Memory.Span); + Color.ToPixel(sourcePalette, this.paletteOwner.Memory.Span); this.ditherProcessor = new DitherProcessor( this.Configuration, diff --git a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs index 9d5b606040..f46cea2e0b 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs @@ -21,7 +21,7 @@ public interface IQuantizer /// The to configure internal operations. /// The pixel format. /// The . - IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) + IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) where TPixel : unmanaged, IPixel; /// @@ -31,6 +31,6 @@ public interface IQuantizer /// The to configure internal operations. /// The options to create the quantizer with. /// The . - IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) + IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel; } diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs index 0a1032bf0d..0d2450e221 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs @@ -34,12 +34,12 @@ public class OctreeQuantizer : IQuantizer public QuantizerOptions Options { get; } /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) + public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) where TPixel : unmanaged, IPixel => this.CreatePixelSpecificQuantizer(configuration, this.Options); /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) + public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel => new OctreeQuantizer(configuration, options); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs index d66a38d80d..37ebc39bac 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs @@ -38,7 +38,7 @@ public struct OctreeQuantizer : IQuantizer /// The configuration which allows altering default behaviour or extending the library. /// The quantizer options defining quantization rules. [MethodImpl(InliningOptions.ShortMethod)] - public OctreeQuantizer(Configuration configuration, QuantizerOptions options) + public OctreeQuantizer(Configuration? configuration, QuantizerOptions options) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(options, nameof(options)); diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index f3ed39c957..c5617fe862 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -39,19 +39,19 @@ public class PaletteQuantizer : IQuantizer public QuantizerOptions Options { get; } /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) + public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) where TPixel : unmanaged, IPixel => this.CreatePixelSpecificQuantizer(configuration, this.Options); /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) + public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel { Guard.NotNull(options, nameof(options)); // Always use the palette length over options since the palette cannot be reduced. TPixel[] palette = new TPixel[this.colorPalette.Length]; - Color.ToPixel(configuration, this.colorPalette.Span, palette.AsSpan()); + Color.ToPixel(this.colorPalette.Span, palette.AsSpan()); return new PaletteQuantizer(configuration, options, palette); } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs index ea7413aab4..182bed2382 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs @@ -29,7 +29,7 @@ internal struct PaletteQuantizer : IQuantizer /// The quantizer options defining quantization rules. /// The palette to use. [MethodImpl(InliningOptions.ShortMethod)] - public PaletteQuantizer(Configuration configuration, QuantizerOptions options, ReadOnlyMemory palette) + public PaletteQuantizer(Configuration? configuration, QuantizerOptions options, ReadOnlyMemory palette) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(options, nameof(options)); diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs index 86d798d965..dff9155401 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs @@ -33,12 +33,12 @@ public class WuQuantizer : IQuantizer public QuantizerOptions Options { get; } /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) + public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) where TPixel : unmanaged, IPixel => this.CreatePixelSpecificQuantizer(configuration, this.Options); /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) + public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel => new WuQuantizer(configuration, options); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs index 0119558bff..c04dbe1918 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs @@ -85,7 +85,7 @@ internal struct WuQuantizer : IQuantizer /// The configuration which allows altering default behaviour or extending the library. /// The quantizer options defining quantization rules. [MethodImpl(InliningOptions.ShortMethod)] - public WuQuantizer(Configuration configuration, QuantizerOptions options) + public WuQuantizer(Configuration? configuration, QuantizerOptions options) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(options, nameof(options)); From cd9a06b4cb6d011b3ebd3e73cfa5944483f6f775 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 17:54:39 +0100 Subject: [PATCH 29/47] Fix unit test. --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 0f432fbda9..e1611b059b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -141,7 +141,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals _ => 0 }; - byte[] iccProfileData = Array.Empty(); + byte[]? iccProfileData = null; int iccProfileSize = 0; if (metadata.IccProfile != null) { @@ -181,7 +181,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The metadata. /// The icc profile data. /// The bitmap information header. - private BmpInfoHeader CreateBmpInfoHeader(int width, int height, int infoHeaderSize, short bpp, int bytesPerLine, ImageMetadata metadata, byte[] iccProfileData) + private BmpInfoHeader CreateBmpInfoHeader(int width, int height, int infoHeaderSize, short bpp, int bytesPerLine, ImageMetadata metadata, byte[]? iccProfileData) { int hResolution = 0; int vResolution = 0; @@ -235,7 +235,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals if (this.infoHeaderType is BmpInfoHeaderType.WinVersion5 && metadata.IccProfile != null) { - infoHeader.ProfileSize = iccProfileData.Length; + infoHeader.ProfileSize = iccProfileData!.Length; infoHeader.CsType = BmpColorSpace.PROFILE_EMBEDDED; infoHeader.Intent = BmpRenderingIntent.LCS_GM_IMAGES; } @@ -249,7 +249,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The stream to write to. /// The color profile data. /// The buffer. - private static void WriteColorProfile(Stream stream, byte[] iccProfileData, Span buffer) + private static void WriteColorProfile(Stream stream, byte[]? iccProfileData, Span buffer) { if (iccProfileData != null) { From 94fd43f71ff34fb714e32f892bd5c37a8928b442 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 18:09:53 +0100 Subject: [PATCH 30/47] remove nullable disable from format gif --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 139 ++++++++++--------- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 24 ++-- src/ImageSharp/Image.Decode.cs | 2 +- src/ImageSharp/Image.cs | 4 +- src/ImageSharp/ImageInfo.cs | 4 +- src/ImageSharp/Image{TPixel}.cs | 4 +- 6 files changed, 90 insertions(+), 87 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 8c59158000..0ccbe3db54 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -24,15 +24,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// private readonly byte[] buffer = new byte[16]; - /// - /// The currently loaded stream. - /// - private BufferedReadStream stream; - /// /// The global color table. /// - private IMemoryOwner globalColorTable; + private IMemoryOwner? globalColorTable; /// /// The area to restore. @@ -77,12 +72,12 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// The abstract metadata. /// - private ImageMetadata metadata; + private ImageMetadata? metadata; /// /// The gif specific metadata. /// - private GifMetadata gifMetadata; + private GifMetadata? gifMetadata; /// /// Initializes a new instance of the class. @@ -108,8 +103,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals where TPixel : unmanaged, IPixel { uint frameCount = 0; - Image image = null; - ImageFrame previousFrame = null; + Image? image = null; + ImageFrame? previousFrame = null; try { this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); @@ -125,7 +120,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals break; } - this.ReadFrame(ref image, ref previousFrame); + this.ReadFrame(stream, ref image, ref previousFrame); // Reset per-frame state. this.imageDescriptor = default; @@ -136,16 +131,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals switch (stream.ReadByte()) { case GifConstants.GraphicControlLabel: - this.ReadGraphicalControlExtension(); + this.ReadGraphicalControlExtension(stream); break; case GifConstants.CommentLabel: - this.ReadComments(); + this.ReadComments(stream); break; case GifConstants.ApplicationExtensionLabel: - this.ReadApplicationExtension(); + this.ReadApplicationExtension(stream); break; case GifConstants.PlainTextLabel: - this.SkipBlock(); // Not supported by any known decoder. + this.SkipBlock(stream); // Not supported by any known decoder. break; } } @@ -187,23 +182,23 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { if (nextFlag == GifConstants.ImageLabel) { - this.ReadImageDescriptor(); + this.ReadImageDescriptor(stream); } else if (nextFlag == GifConstants.ExtensionIntroducer) { switch (stream.ReadByte()) { case GifConstants.GraphicControlLabel: - this.SkipBlock(); // Skip graphic control extension block + this.SkipBlock(stream); // Skip graphic control extension block break; case GifConstants.CommentLabel: - this.ReadComments(); + this.ReadComments(stream); break; case GifConstants.ApplicationExtensionLabel: - this.ReadApplicationExtension(); + this.ReadApplicationExtension(stream); break; case GifConstants.PlainTextLabel: - this.SkipBlock(); // Not supported by any known decoder. + this.SkipBlock(stream); // Not supported by any known decoder. break; } } @@ -239,9 +234,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// Reads the graphic control extension. /// - private void ReadGraphicalControlExtension() + /// The containing image data. + private void ReadGraphicalControlExtension(BufferedReadStream stream) { - int bytesRead = this.stream.Read(this.buffer, 0, 6); + int bytesRead = stream.Read(this.buffer, 0, 6); if (bytesRead != 6) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension"); @@ -253,9 +249,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// Reads the image descriptor. /// - private void ReadImageDescriptor() + /// The containing image data. + private void ReadImageDescriptor(BufferedReadStream stream) { - int bytesRead = this.stream.Read(this.buffer, 0, 9); + int bytesRead = stream.Read(this.buffer, 0, 9); if (bytesRead != 9) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor"); @@ -271,9 +268,10 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// Reads the logical screen descriptor. /// - private void ReadLogicalScreenDescriptor() + /// The containing image data. + private void ReadLogicalScreenDescriptor(BufferedReadStream stream) { - int bytesRead = this.stream.Read(this.buffer, 0, 7); + int bytesRead = stream.Read(this.buffer, 0, 7); if (bytesRead != 7) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor"); @@ -286,84 +284,87 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// Reads the application extension block parsing any animation or XMP information /// if present. /// - private void ReadApplicationExtension() + /// The containing image data. + private void ReadApplicationExtension(BufferedReadStream stream) { - int appLength = this.stream.ReadByte(); + int appLength = stream.ReadByte(); // If the length is 11 then it's a valid extension and most likely // a NETSCAPE, XMP or ANIMEXTS extension. We want the loop count from this. - long position = this.stream.Position; + long position = stream.Position; if (appLength == GifConstants.ApplicationBlockSize) { - this.stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize); + stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize); bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes); if (isXmp && !this.skipMetadata) { - GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(this.stream, this.memoryAllocator); + GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(stream, this.memoryAllocator); if (extension.Data.Length > 0) { - this.metadata.XmpProfile = new XmpProfile(extension.Data); + this.metadata!.XmpProfile = new XmpProfile(extension.Data); } else { // Reset the stream position and continue. - this.stream.Position = position; - this.SkipBlock(appLength); + stream.Position = position; + this.SkipBlock(stream, appLength); } return; } - int subBlockSize = this.stream.ReadByte(); + int subBlockSize = stream.ReadByte(); // TODO: There's also a NETSCAPE buffer extension. // http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize) { - this.stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize); - this.gifMetadata.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount; - this.stream.Skip(1); // Skip the terminator. + stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize); + this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount; + stream.Skip(1); // Skip the terminator. return; } // Could be something else not supported yet. // Skip the subblock and terminator. - this.SkipBlock(subBlockSize); + this.SkipBlock(stream, subBlockSize); return; } - this.SkipBlock(appLength); // Not supported by any known decoder. + this.SkipBlock(stream, appLength); // Not supported by any known decoder. } /// /// Skips over a block or reads its terminator. /// The length of the block to skip. + /// The containing image data. /// - private void SkipBlock(int blockSize = 0) + private void SkipBlock(BufferedReadStream stream, int blockSize = 0) { if (blockSize > 0) { - this.stream.Skip(blockSize); + stream.Skip(blockSize); } int flag; - while ((flag = this.stream.ReadByte()) > 0) + while ((flag = stream.ReadByte()) > 0) { - this.stream.Skip(flag); + stream.Skip(flag); } } /// /// Reads the gif comments. /// - private void ReadComments() + /// The containing image data. + private void ReadComments(BufferedReadStream stream) { int length; var stringBuilder = new StringBuilder(); - while ((length = this.stream.ReadByte()) != 0) + while ((length = stream.ReadByte()) != 0) { if (length > GifConstants.MaxCommentSubBlockLength) { @@ -372,21 +373,21 @@ internal sealed class GifDecoderCore : IImageDecoderInternals if (this.skipMetadata) { - this.stream.Seek(length, SeekOrigin.Current); + stream.Seek(length, SeekOrigin.Current); continue; } using IMemoryOwner commentsBuffer = this.memoryAllocator.Allocate(length); Span commentsSpan = commentsBuffer.GetSpan(); - this.stream.Read(commentsSpan); + stream.Read(commentsSpan); string commentPart = GifConstants.Encoding.GetString(commentsSpan); stringBuilder.Append(commentPart); } if (stringBuilder.Length > 0) { - this.gifMetadata.Comments.Add(stringBuilder.ToString()); + this.gifMetadata!.Comments.Add(stringBuilder.ToString()); } } @@ -394,15 +395,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// Reads an individual gif frame. /// /// The pixel format. + /// The containing image data. /// The image to decode the information to. /// The previous frame. - private void ReadFrame(ref Image image, ref ImageFrame previousFrame) + private void ReadFrame(BufferedReadStream stream, ref Image? image, ref ImageFrame? previousFrame) where TPixel : unmanaged, IPixel { - this.ReadImageDescriptor(); + this.ReadImageDescriptor(stream); - IMemoryOwner localColorTable = null; - Buffer2D indices = null; + IMemoryOwner? localColorTable = null; + Buffer2D? indices = null; try { // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. @@ -410,11 +412,11 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { int length = this.imageDescriptor.LocalColorTableSize * 3; localColorTable = this.configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean); - this.stream.Read(localColorTable.GetSpan()); + stream.Read(localColorTable.GetSpan()); } indices = this.configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); - this.ReadFrameIndices(indices); + this.ReadFrameIndices(stream, indices); Span rawColorTable = default; if (localColorTable != null) @@ -430,7 +432,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor); // Skip any remaining blocks - this.SkipBlock(); + this.SkipBlock(stream); } finally { @@ -442,12 +444,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// Reads the frame indices marking the color to use for each pixel. /// + /// The containing image data. /// The 2D pixel buffer to write to. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadFrameIndices(Buffer2D indices) + private void ReadFrameIndices(BufferedReadStream stream, Buffer2D indices) { - int minCodeSize = this.stream.ReadByte(); - using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream); + int minCodeSize = stream.ReadByte(); + using LzwDecoder lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, stream); lzwDecoder.DecodePixels(minCodeSize, indices); } @@ -460,15 +463,15 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// The indexed pixels. /// The color table containing the available colors. /// The - private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Buffer2D indices, ReadOnlySpan colorTable, in GifImageDescriptor descriptor) + private void ReadFrameColors(ref Image? image, ref ImageFrame? previousFrame, Buffer2D indices, ReadOnlySpan colorTable, in GifImageDescriptor descriptor) where TPixel : unmanaged, IPixel { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; bool transFlag = this.graphicsControlExtension.TransparencyFlag; - ImageFrame prevFrame = null; - ImageFrame currentFrame = null; + ImageFrame? prevFrame = null; + ImageFrame? currentFrame = null; ImageFrame imageFrame; if (previousFrame is null) @@ -494,7 +497,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals prevFrame = previousFrame; } - currentFrame = image.Frames.CreateFrame(); + currentFrame = image!.Frames.CreateFrame(); this.SetFrameMetadata(currentFrame.Metadata, false); @@ -661,13 +664,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// Reads the logical screen descriptor and global color table blocks /// /// The stream containing image data. + [MemberNotNull(nameof(metadata))] + [MemberNotNull(nameof(gifMetadata))] private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream) { - this.stream = stream; - // Skip the identifier - this.stream.Skip(6); - this.ReadLogicalScreenDescriptor(); + stream.Skip(6); + this.ReadLogicalScreenDescriptor(stream); ImageMetadata meta = new(); diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index eaa6852937..0e59a0f05e 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; @@ -93,7 +93,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; // Quantize the image returning a palette. - IndexedImageFrame quantized; + IndexedImageFrame? quantized; using (IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration)) { if (useGlobalTable) @@ -129,7 +129,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals this.WriteComments(gifMetadata, stream); // Write application extensions. - XmpProfile xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; + XmpProfile? xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; this.WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, xmpProfile); } @@ -141,7 +141,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals private void EncodeFrames( Stream stream, Image image, - IndexedImageFrame quantized, + IndexedImageFrame? quantized, ReadOnlyMemory palette) where TPixel : unmanaged, IPixel { @@ -152,8 +152,8 @@ internal sealed class GifEncoderCore : IImageEncoderInternals // Gather the metadata for this frame. ImageFrame frame = image.Frames[i]; ImageFrameMetadata metadata = frame.Metadata; - bool hasMetadata = metadata.TryGetGifMetadata(out GifFrameMetadata frameMetadata); - bool useLocal = this.colorTableMode == GifColorTableMode.Local || (hasMetadata && frameMetadata.ColorTableMode == GifColorTableMode.Local); + bool hasMetadata = metadata.TryGetGifMetadata(out GifFrameMetadata? frameMetadata); + bool useLocal = this.colorTableMode == GifColorTableMode.Local || (hasMetadata && frameMetadata!.ColorTableMode == GifColorTableMode.Local); if (!useLocal && !hasPaletteQuantizer && i > 0) { @@ -164,7 +164,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals paletteQuantizer = new(this.configuration, this.quantizer.Options, palette); } - this.EncodeFrame(stream, frame, i, useLocal, frameMetadata, ref quantized, ref paletteQuantizer); + this.EncodeFrame(stream, frame, i, useLocal, frameMetadata, ref quantized!, ref paletteQuantizer); // Clean up for the next run. quantized.Dispose(); @@ -182,7 +182,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals ImageFrame frame, int frameIndex, bool useLocal, - GifFrameMetadata metadata, + GifFrameMetadata? metadata, ref IndexedImageFrame quantized, ref PaletteQuantizer paletteQuantizer) where TPixel : unmanaged, IPixel @@ -193,7 +193,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals if (useLocal) { // Reassign using the current frame and details. - QuantizerOptions options = null; + QuantizerOptions? options = null; int colorTableLength = metadata?.ColorTableLength ?? 0; if (colorTableLength > 0) { @@ -338,7 +338,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals /// The frame count fo this image. /// The animated image repeat count. /// The XMP metadata profile. Null if profile is not to be written. - private void WriteApplicationExtensions(Stream stream, int frameCount, ushort repeatCount, XmpProfile xmpProfile) + private void WriteApplicationExtensions(Stream stream, int frameCount, ushort repeatCount, XmpProfile? xmpProfile) { // Application Extension: Loop repeat count. if (frameCount > 1 && repeatCount != 1) @@ -350,7 +350,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals // Application Extension: XMP Profile. if (xmpProfile != null) { - GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data); + GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data!); this.WriteExtension(xmpExtension, stream); } } @@ -439,7 +439,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals private void WriteExtension(TGifExtension extension, Stream stream) where TGifExtension : struct, IGifExtension { - IMemoryOwner owner = null; + IMemoryOwner? owner = null; Span extensionBuffer; int extensionSize = extension.ContentLength; diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index ae38144c02..887cb23ca4 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -91,7 +91,7 @@ public abstract partial class Image ImageFormatManager.ThrowInvalidDecoder(configuration.ImageFormatsManager); } - return format!; + return format; } /// diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 65b2d68b0a..af1b1916f9 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -28,7 +28,7 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv /// The pixel type information. /// The image metadata. /// The size in px units. - protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, Size size) + protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata? metadata, Size size) : base(pixelType, size, metadata) => this.configuration = configuration ?? Configuration.Default; @@ -43,7 +43,7 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv internal Image( Configuration configuration, PixelTypeInfo pixelType, - ImageMetadata metadata, + ImageMetadata? metadata, int width, int height) : this(configuration, pixelType, metadata, new Size(width, height)) diff --git a/src/ImageSharp/ImageInfo.cs b/src/ImageSharp/ImageInfo.cs index 9a6452ab89..fdc15a8127 100644 --- a/src/ImageSharp/ImageInfo.cs +++ b/src/ImageSharp/ImageInfo.cs @@ -18,7 +18,7 @@ public class ImageInfo /// The width of the image in px units. /// The height of the image in px units. /// The image metadata. - public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata metadata) + public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata? metadata) : this(pixelType, new(width, height), metadata) { } @@ -29,7 +29,7 @@ public class ImageInfo /// The pixel type information. /// The size of the image in px units. /// The image metadata. - public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata metadata) + public ImageInfo(PixelTypeInfo pixelType, Size size, ImageMetadata? metadata) { this.PixelType = pixelType; this.Metadata = metadata ?? new ImageMetadata(); diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 9e3cc065c0..ed0db9b85b 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -77,7 +77,7 @@ public sealed class Image : Image /// The width of the image in pixels. /// The height of the image in pixels. /// The images metadata. - internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) + internal Image(Configuration configuration, int width, int height, ImageMetadata? metadata) : base(configuration, PixelTypeInfo.Create(), metadata, width, height) => this.frames = new ImageFrameCollection(this, width, height, default(TPixel)); @@ -128,7 +128,7 @@ public sealed class Image : Image int width, int height, TPixel backgroundColor, - ImageMetadata metadata) + ImageMetadata? metadata) : base(configuration, PixelTypeInfo.Create(), metadata, width, height) => this.frames = new ImageFrameCollection(this, width, height, backgroundColor); From 90ac3a29c9292d14ec217033c75140238262ba2e Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 18:40:45 +0100 Subject: [PATCH 31/47] Make skipblock static to fix build --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 0ccbe3db54..6c82d61e1d 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -140,7 +140,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals this.ReadApplicationExtension(stream); break; case GifConstants.PlainTextLabel: - this.SkipBlock(stream); // Not supported by any known decoder. + SkipBlock(stream); // Not supported by any known decoder. break; } } @@ -189,7 +189,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals switch (stream.ReadByte()) { case GifConstants.GraphicControlLabel: - this.SkipBlock(stream); // Skip graphic control extension block + SkipBlock(stream); // Skip graphic control extension block break; case GifConstants.CommentLabel: this.ReadComments(stream); @@ -198,7 +198,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals this.ReadApplicationExtension(stream); break; case GifConstants.PlainTextLabel: - this.SkipBlock(stream); // Not supported by any known decoder. + SkipBlock(stream); // Not supported by any known decoder. break; } } @@ -307,7 +307,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { // Reset the stream position and continue. stream.Position = position; - this.SkipBlock(stream, appLength); + SkipBlock(stream, appLength); } return; @@ -327,12 +327,12 @@ internal sealed class GifDecoderCore : IImageDecoderInternals // Could be something else not supported yet. // Skip the subblock and terminator. - this.SkipBlock(stream, subBlockSize); + SkipBlock(stream, subBlockSize); return; } - this.SkipBlock(stream, appLength); // Not supported by any known decoder. + SkipBlock(stream, appLength); // Not supported by any known decoder. } /// @@ -340,7 +340,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// The length of the block to skip. /// The containing image data. /// - private void SkipBlock(BufferedReadStream stream, int blockSize = 0) + private static void SkipBlock(BufferedReadStream stream, int blockSize = 0) { if (blockSize > 0) { @@ -363,7 +363,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { int length; - var stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder(); while ((length = stream.ReadByte()) != 0) { if (length > GifConstants.MaxCommentSubBlockLength) @@ -432,7 +432,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor); // Skip any remaining blocks - this.SkipBlock(stream); + SkipBlock(stream); } finally { From f9ac6624b0759977a7daa90a35a42fdfff7b2bfe Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 19:18:34 +0100 Subject: [PATCH 32/47] remove nullable disable from Format Pbm --- src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs index c5e0b00718..80db009a44 100644 --- a/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmDecoderCore.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -45,7 +44,7 @@ internal sealed class PbmDecoderCore : IImageDecoderInternals /// /// The decoded by this decoder instance. /// - private ImageMetadata metadata; + private ImageMetadata? metadata; /// /// Initializes a new instance of the class. From 94ebf3627d018cb8bd74a2602ee7c50d9f8aca54 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 19:43:59 +0100 Subject: [PATCH 33/47] Remove nullable disable from format tga --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 160 +++++++++---------- src/ImageSharp/Formats/Tga/TgaEncoderCore.cs | 3 +- 2 files changed, 76 insertions(+), 87 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 9c32471fd2..292ffccd96 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -29,12 +29,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// /// The metadata. /// - private ImageMetadata metadata; + private ImageMetadata metadata = null!; /// /// The tga specific metadata. /// - private TgaMetadata tgaMetadata; + private TgaMetadata tgaMetadata = null!; /// /// The file header containing general information about the image. @@ -46,11 +46,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// private readonly MemoryAllocator memoryAllocator; - /// - /// The stream to decode from. - /// - private BufferedReadStream currentStream; - /// /// Indicates whether there is a alpha channel present. /// @@ -80,7 +75,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals try { TgaImageOrigin origin = this.ReadFileHeader(stream); - this.currentStream.Skip(this.fileHeader.IdLength); + stream.Skip(this.fileHeader.IdLength); // Parse the color map, if present. if (this.fileHeader.ColorMapType is not 0 and not 1) @@ -93,7 +88,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals throw new UnknownImageFormatException("Width or height cannot be 0"); } - var image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); + Image image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); if (this.fileHeader.ColorMapType == 1) @@ -113,7 +108,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals using (IMemoryOwner palette = this.memoryAllocator.Allocate(colorMapSizeInBytes, AllocationOptions.Clean)) { Span paletteSpan = palette.GetSpan(); - int bytesRead = this.currentStream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes); + int bytesRead = stream.Read(paletteSpan, this.fileHeader.CMapStart, colorMapSizeInBytes); if (bytesRead != colorMapSizeInBytes) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read the color map"); @@ -122,6 +117,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals if (this.fileHeader.ImageType == TgaImageType.RleColorMapped) { this.ReadPalettedRle( + stream, this.fileHeader.Width, this.fileHeader.Height, pixels, @@ -132,6 +128,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals else { this.ReadPaletted( + stream, this.fileHeader.Width, this.fileHeader.Height, pixels, @@ -148,7 +145,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals if (this.fileHeader.CMapLength > 0) { int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8; - this.currentStream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes); + stream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes); } switch (this.fileHeader.PixelDepth) @@ -156,11 +153,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals case 8: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin); + this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 1, origin); } else { - this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); + this.ReadMonoChrome(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -169,11 +166,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals case 16: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin); + this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 2, origin); } else { - this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); + this.ReadBgra16(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -181,11 +178,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals case 24: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin); + this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 3, origin); } else { - this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); + this.ReadBgr24(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -193,11 +190,11 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals case 32: if (this.fileHeader.ImageType.IsRunLengthEncoded()) { - this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin); + this.ReadRle(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, 4, origin); } else { - this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, origin); + this.ReadBgra32(stream, this.fileHeader.Width, this.fileHeader.Height, pixels, origin); } break; @@ -219,13 +216,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// Reads a uncompressed TGA image with a palette. /// /// The pixel type. + /// The containing image data. /// The width of the image. /// The height of the image. /// The to assign the palette to. /// The color palette. /// Color map size of one entry in bytes. /// The image origin. - private void ReadPaletted(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) + private void ReadPaletted(BufferedReadStream stream, int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -243,14 +241,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } else { for (int x = 0; x < width; x++) { - this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgra16Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -261,14 +259,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); - } - } - else - { - for (int x = 0; x < width; x++) - { - this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -279,14 +270,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); - } - } - else - { - for (int x = 0; x < width; x++) - { - this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + this.ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -299,20 +283,21 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// Reads a run length encoded TGA image with a palette. /// /// The pixel type. + /// The containing image data. /// The width of the image. /// The height of the image. /// The to assign the palette to. /// The color palette. /// Color map size of one entry in bytes. /// The image origin. - private void ReadPalettedRle(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) + private void ReadPalettedRle(BufferedReadStream stream, int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean)) { TPixel color = default; Span bufferSpan = buffer.GetSpan(); - this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1); + this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel: 1); for (int y = 0; y < height; y++) { @@ -349,11 +334,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// Reads a uncompressed monochrome TGA image. /// /// The pixel type. + /// The containing image data. /// The width of the image. /// The height of the image. /// The to assign the palette to. /// the image origin. - private void ReadMonoChrome(int width, int height, Buffer2D pixels, TgaImageOrigin origin) + private void ReadMonoChrome(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { bool invertX = InvertX(origin); @@ -366,7 +352,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Span pixelSpan = pixels.DangerousGetRowSpan(newY); for (int x = width - 1; x >= 0; x--) { - this.ReadL8Pixel(color, x, pixelSpan); + this.ReadL8Pixel(stream, color, x, pixelSpan); } } @@ -380,14 +366,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int y = height - 1; y >= 0; y--) { - this.ReadL8Row(width, pixels, rowSpan, y); + this.ReadL8Row(stream, width, pixels, rowSpan, y); } } else { for (int y = 0; y < height; y++) { - this.ReadL8Row(width, pixels, rowSpan, y); + this.ReadL8Row(stream, width, pixels, rowSpan, y); } } } @@ -396,11 +382,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// Reads a uncompressed TGA image where each pixels has 16 bit. /// /// The pixel type. + /// The containing image data. /// The width of the image. /// The height of the image. /// The to assign the palette to. /// The image origin. - private void ReadBgra16(int width, int height, Buffer2D pixels, TgaImageOrigin origin) + private void ReadBgra16(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -417,7 +404,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 2); + int bytesRead = stream.Read(this.scratchBuffer, 0, 2); if (bytesRead != 2) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); @@ -442,7 +429,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } else { - int bytesRead = this.currentStream.Read(rowSpan); + int bytesRead = stream.Read(rowSpan); if (bytesRead != rowSpan.Length) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); @@ -473,11 +460,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// Reads a uncompressed TGA image where each pixels has 24 bit. /// /// The pixel type. + /// The containing image data. /// The width of the image. /// The height of the image. /// The to assign the palette to. /// The image origin. - private void ReadBgr24(int width, int height, Buffer2D pixels, TgaImageOrigin origin) + private void ReadBgr24(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { bool invertX = InvertX(origin); @@ -490,7 +478,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Span pixelSpan = pixels.DangerousGetRowSpan(newY); for (int x = width - 1; x >= 0; x--) { - this.ReadBgr24Pixel(color, x, pixelSpan); + this.ReadBgr24Pixel(stream, color, x, pixelSpan); } } @@ -505,14 +493,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int y = height - 1; y >= 0; y--) { - this.ReadBgr24Row(width, pixels, rowSpan, y); + this.ReadBgr24Row(stream, width, pixels, rowSpan, y); } } else { for (int y = 0; y < height; y++) { - this.ReadBgr24Row(width, pixels, rowSpan, y); + this.ReadBgr24Row(stream, width, pixels, rowSpan, y); } } } @@ -521,11 +509,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// Reads a uncompressed TGA image where each pixels has 32 bit. /// /// The pixel type. + /// The containing image data. /// The width of the image. /// The height of the image. /// The to assign the palette to. /// The image origin. - private void ReadBgra32(int width, int height, Buffer2D pixels, TgaImageOrigin origin) + private void ReadBgra32(BufferedReadStream stream, int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -539,14 +528,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int y = height - 1; y >= 0; y--) { - this.ReadBgra32Row(width, pixels, rowSpan, y); + this.ReadBgra32Row(stream, width, pixels, rowSpan, y); } } else { for (int y = 0; y < height; y++) { - this.ReadBgra32Row(width, pixels, rowSpan, y); + this.ReadBgra32Row(stream, width, pixels, rowSpan, y); } } @@ -561,14 +550,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadBgra32Pixel(x, color, pixelRow); + this.ReadBgra32Pixel(stream, x, color, pixelRow); } } else { for (int x = 0; x < width; x++) { - this.ReadBgra32Pixel(x, color, pixelRow); + this.ReadBgra32Pixel(stream, x, color, pixelRow); } } } @@ -578,12 +567,13 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// Reads a run length encoded TGA image. /// /// The pixel type. + /// The containing image data. /// The width of the image. /// The height of the image. /// The to assign the palette to. /// The bytes per pixel. /// The image origin. - private void ReadRle(int width, int height, Buffer2D pixels, int bytesPerPixel, TgaImageOrigin origin) + private void ReadRle(BufferedReadStream stream, int width, int height, Buffer2D pixels, int bytesPerPixel, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel color = default; @@ -591,7 +581,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) { Span bufferSpan = buffer.GetSpan(); - this.UncompressRle(width, height, bufferSpan, bytesPerPixel); + this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel); for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); @@ -658,10 +648,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadL8Row(int width, Buffer2D pixels, Span row, int y) + private void ReadL8Row(BufferedReadStream stream, int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { - int bytesRead = this.currentStream.Read(row); + int bytesRead = stream.Read(row); if (bytesRead != row.Length) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); @@ -672,19 +662,19 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadL8Pixel(TPixel color, int x, Span pixelSpan) + private void ReadL8Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { - byte pixelValue = (byte)this.currentStream.ReadByte(); + byte pixelValue = (byte)stream.ReadByte(); color.FromL8(Unsafe.As(ref pixelValue)); pixelSpan[x] = color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadBgr24Pixel(TPixel color, int x, Span pixelSpan) + private void ReadBgr24Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { - int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 3); + int bytesRead = stream.Read(this.scratchBuffer, 0, 3); if (bytesRead != 3) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel"); @@ -695,10 +685,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadBgr24Row(int width, Buffer2D pixels, Span row, int y) + private void ReadBgr24Row(BufferedReadStream stream, int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { - int bytesRead = this.currentStream.Read(row); + int bytesRead = stream.Read(row); if (bytesRead != row.Length) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); @@ -709,10 +699,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadBgra32Pixel(int x, TPixel color, Span pixelRow) + private void ReadBgra32Pixel(BufferedReadStream stream, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { - int bytesRead = this.currentStream.Read(this.scratchBuffer, 0, 4); + int bytesRead = stream.Read(this.scratchBuffer, 0, 4); if (bytesRead != 4) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel"); @@ -724,10 +714,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadBgra32Row(int width, Buffer2D pixels, Span row, int y) + private void ReadBgra32Row(BufferedReadStream stream, int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { - int bytesRead = this.currentStream.Read(row); + int bytesRead = stream.Read(row); if (bytesRead != row.Length) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); @@ -738,10 +728,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgra16Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private void ReadPalettedBgra16Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { - int colorIndex = this.currentStream.ReadByte(); + int colorIndex = stream.ReadByte(); if (colorIndex == -1) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); @@ -768,10 +758,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgr24Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private void ReadPalettedBgr24Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { - int colorIndex = this.currentStream.ReadByte(); + int colorIndex = stream.ReadByte(); if (colorIndex == -1) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); @@ -782,10 +772,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgra32Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private void ReadPalettedBgra32Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { - int colorIndex = this.currentStream.ReadByte(); + int colorIndex = stream.ReadByte(); if (colorIndex == -1) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); @@ -802,21 +792,21 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// The height of the image. /// Buffer for uncompressed data. /// The bytes used per pixel. - private void UncompressRle(int width, int height, Span buffer, int bytesPerPixel) + private void UncompressRle(BufferedReadStream stream, int width, int height, Span buffer, int bytesPerPixel) { int uncompressedPixels = 0; Span pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel); int totalPixels = width * height; while (uncompressedPixels < totalPixels) { - byte runLengthByte = (byte)this.currentStream.ReadByte(); + byte runLengthByte = (byte)stream.ReadByte(); // The high bit of a run length packet is set to 1. int highBit = runLengthByte >> 7; if (highBit == 1) { int runLength = runLengthByte & 127; - int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel); + int bytesRead = stream.Read(pixel, 0, bytesPerPixel); if (bytesRead != bytesPerPixel) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); @@ -836,7 +826,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals int bufferIdx = uncompressedPixels * bytesPerPixel; for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) { - int bytesRead = this.currentStream.Read(pixel, 0, bytesPerPixel); + int bytesRead = stream.Read(pixel, 0, bytesPerPixel); if (bytesRead != bytesPerPixel) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); @@ -917,13 +907,13 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// /// The containing image data. /// The image origin. + [MemberNotNull(nameof(metadata))] + [MemberNotNull(nameof(tgaMetadata))] private TgaImageOrigin ReadFileHeader(BufferedReadStream stream) { - this.currentStream = stream; - Span buffer = stackalloc byte[TgaFileHeader.Size]; - this.currentStream.Read(buffer, 0, TgaFileHeader.Size); + stream.Read(buffer, 0, TgaFileHeader.Size); this.fileHeader = TgaFileHeader.Parse(buffer); this.metadata = new ImageMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata(); @@ -939,7 +929,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals this.hasAlpha = alphaBits > 0; // Bits 4 and 5 describe the image origin. - var origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); + TgaImageOrigin origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); return origin; } } diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index 44799c894b..0b987a3d6c 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Buffers.Binary; @@ -26,7 +25,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// /// The global configuration. /// - private Configuration configuration; + private Configuration? configuration; /// /// Reusable buffer for writing data. From 7be55a1917d1f8a46836c56c850986973d0183ba Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 21:53:59 +0100 Subject: [PATCH 34/47] Fix build --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 292ffccd96..7ca5fa9e66 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -259,7 +259,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -270,7 +270,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -352,7 +352,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Span pixelSpan = pixels.DangerousGetRowSpan(newY); for (int x = width - 1; x >= 0; x--) { - this.ReadL8Pixel(stream, color, x, pixelSpan); + ReadL8Pixel(stream, color, x, pixelSpan); } } @@ -662,7 +662,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadL8Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan) + private static void ReadL8Pixel(BufferedReadStream stream, TPixel color, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { byte pixelValue = (byte)stream.ReadByte(); @@ -758,7 +758,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgr24Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private static void ReadPalettedBgr24Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { int colorIndex = stream.ReadByte(); @@ -772,7 +772,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgra32Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) + private static void ReadPalettedBgra32Pixel(BufferedReadStream stream, Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { int colorIndex = stream.ReadByte(); @@ -788,6 +788,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// /// Produce uncompressed tga data from a run length encoded stream. /// + /// The containing image data. /// The width of the image. /// The height of the image. /// Buffer for uncompressed data. From 72cee824a0b3a140944a97048b76c298572896e0 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 5 Feb 2023 22:41:16 +0100 Subject: [PATCH 35/47] Readd deleted code... --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 7ca5fa9e66..65ab8dfc15 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -262,6 +262,13 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } + else + { + for (int x = 0; x < width; x++) + { + ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + } + } break; @@ -273,6 +280,13 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } + else + { + for (int x = 0; x < width; x++) + { + ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); + } + } break; } From 7b46791c41f425a1b2a0c24be6d424b53c3943c1 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Mon, 6 Feb 2023 09:18:44 +0100 Subject: [PATCH 36/47] Remove pragma --- src/ImageSharp/Color/Color.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 590a3ed311..13af25f6c7 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -289,9 +289,7 @@ public readonly partial struct Color : IEquatable /// The source color span. /// The destination pixel span. [MethodImpl(InliningOptions.ShortMethod)] -#pragma warning disable RCS1163 // Unused parameter. public static void ToPixel(ReadOnlySpan source, Span destination) -#pragma warning restore RCS1163 // Unused parameter. where TPixel : unmanaged, IPixel { // TODO: Investigate bulk operations utilizing configuration parameter here. From cc4647741ae58a17280ef35d51c5e22c056dd156 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Mon, 6 Feb 2023 10:43:26 +0100 Subject: [PATCH 37/47] Pass configuration as parameter --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 79 ++++++++++--------- .../Processors/Quantization/IQuantizer.cs | 4 +- .../Quantization/OctreeQuantizer.cs | 4 +- .../Quantization/PaletteQuantizer.cs | 4 +- .../Processors/Quantization/WuQuantizer.cs | 4 +- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index e1611b059b..eaf6b4df7f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -68,11 +68,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// private readonly MemoryAllocator memoryAllocator; - /// - /// The global configuration. - /// - private Configuration? configuration; - /// /// The color depth, in number of bits per pixel. /// @@ -123,7 +118,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - this.configuration = image.GetConfiguration(); + Configuration configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); this.bitsPerPixel ??= bmpMetadata.BitsPerPixel; @@ -164,7 +159,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); - this.WriteImage(stream, image); + this.WriteImage(stream, image, configuration); WriteColorProfile(stream, iccProfileData, buffer); stream.Flush(); @@ -316,38 +311,39 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// /// The containing pixel data. /// - private void WriteImage(Stream stream, Image image) + /// The global configuration. + private void WriteImage(Stream stream, Image image, Configuration configuration) where TPixel : unmanaged, IPixel { Buffer2D pixels = image.Frames.RootFrame.PixelBuffer; switch (this.bitsPerPixel) { case BmpBitsPerPixel.Pixel32: - this.Write32BitPixelData(stream, pixels); + this.Write32BitPixelData(stream, pixels, configuration); break; case BmpBitsPerPixel.Pixel24: - this.Write24BitPixelData(stream, pixels); + this.Write24BitPixelData(stream, pixels, configuration); break; case BmpBitsPerPixel.Pixel16: - this.Write16BitPixelData(stream, pixels); + this.Write16BitPixelData(stream, pixels, configuration); break; case BmpBitsPerPixel.Pixel8: - this.Write8BitPixelData(stream, image); + this.Write8BitPixelData(stream, image, configuration); break; case BmpBitsPerPixel.Pixel4: - this.Write4BitPixelData(stream, image); + this.Write4BitPixelData(stream, image, configuration); break; case BmpBitsPerPixel.Pixel2: - this.Write2BitPixelData(stream, image); + this.Write2BitPixelData(stream, image, configuration); break; case BmpBitsPerPixel.Pixel1: - this.Write1BitPixelData(stream, image); + this.Write1BitPixelData(stream, image, configuration); break; } } @@ -361,7 +357,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The pixel format. /// The to write to. /// The containing pixel data. - private void Write32BitPixelData(Stream stream, Buffer2D pixels) + /// The global configuration. + private void Write32BitPixelData(Stream stream, Buffer2D pixels, Configuration configuration) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 4); @@ -371,7 +368,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra32Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -385,7 +382,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The pixel format. /// The to write to. /// The containing pixel data. - private void Write24BitPixelData(Stream stream, Buffer2D pixels) + /// The global configuration. + private void Write24BitPixelData(Stream stream, Buffer2D pixels, Configuration configuration) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -397,7 +395,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgr24Bytes( - this.configuration, + configuration, pixelSpan, row.Slice(0, rowBytesWithoutPadding), width); @@ -411,7 +409,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write16BitPixelData(Stream stream, Buffer2D pixels) + /// The global configuration. + private void Write16BitPixelData(Stream stream, Buffer2D pixels, Configuration configuration) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -424,7 +423,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra5551Bytes( - this.configuration, + configuration, pixelSpan, row.Slice(0, rowBytesWithoutPadding), pixelSpan.Length); @@ -439,7 +438,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write8BitPixelData(Stream stream, Image image) + /// The global configuration. + private void Write8BitPixelData(Stream stream, Image image, Configuration configuration) where TPixel : unmanaged, IPixel { bool isL8 = typeof(TPixel) == typeof(L8); @@ -452,7 +452,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals } else { - this.Write8BitColor(stream, image, colorPalette); + this.Write8BitColor(stream, image, colorPalette, configuration); } } @@ -463,16 +463,17 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The to write to. /// The containing pixel data. /// A byte span of size 1024 for the color palette. - private void Write8BitColor(Stream stream, Image image, Span colorPalette) + /// The global configuration + private void Write8BitColor(Stream stream, Image image, Span colorPalette, Configuration configuration) where TPixel : unmanaged, IPixel { - using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration); + using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration); frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image); using IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); for (int y = image.Height - 1; y >= 0; y--) { @@ -530,10 +531,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write4BitPixelData(Stream stream, Image image) + /// The global configuration. + private void Write4BitPixelData(Stream stream, Image image, Configuration configuration) where TPixel : unmanaged, IPixel { - using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() + using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions() { MaxColors = 16 }); @@ -545,7 +547,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding; @@ -577,10 +579,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write2BitPixelData(Stream stream, Image image) + /// The global configuration + private void Write2BitPixelData(Stream stream, Image image, Configuration configuration) where TPixel : unmanaged, IPixel { - using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() + using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions() { MaxColors = 4 }); @@ -592,7 +595,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; @@ -633,10 +636,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write1BitPixelData(Stream stream, Image image) + /// The global configuration + private void Write1BitPixelData(Stream stream, Image image, Configuration configuration) where TPixel : unmanaged, IPixel { - using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() + using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions() { MaxColors = 2 }); @@ -648,7 +652,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); ReadOnlySpan quantizedPixelRow = quantized.DangerousGetRowSpan(0); int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding; @@ -683,11 +687,12 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The to write to. /// The color palette from the quantized image. /// A temporary byte span to write the color palette to. - private void WriteColorPalette(Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette) + /// The global configuration + private void WriteColorPalette(Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette, Configuration configuration) where TPixel : unmanaged, IPixel { int quantizedColorBytes = quantizedColorPalette.Length * 4; - PixelOperations.Instance.ToBgra32(this.configuration, quantizedColorPalette, MemoryMarshal.Cast(colorPalette[..quantizedColorBytes])); + PixelOperations.Instance.ToBgra32(configuration, quantizedColorPalette, MemoryMarshal.Cast(colorPalette[..quantizedColorBytes])); Span colorPaletteAsUInt = MemoryMarshal.Cast(colorPalette); for (int i = 0; i < colorPaletteAsUInt.Length; i++) { diff --git a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs index f46cea2e0b..9d5b606040 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs @@ -21,7 +21,7 @@ public interface IQuantizer /// The to configure internal operations. /// The pixel format. /// The . - IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) + IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel; /// @@ -31,6 +31,6 @@ public interface IQuantizer /// The to configure internal operations. /// The options to create the quantizer with. /// The . - IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) + IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel; } diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs index 0d2450e221..0a1032bf0d 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs @@ -34,12 +34,12 @@ public class OctreeQuantizer : IQuantizer public QuantizerOptions Options { get; } /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel => this.CreatePixelSpecificQuantizer(configuration, this.Options); /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel => new OctreeQuantizer(configuration, options); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index c5617fe862..fe4af9005a 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -39,12 +39,12 @@ public class PaletteQuantizer : IQuantizer public QuantizerOptions Options { get; } /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel => this.CreatePixelSpecificQuantizer(configuration, this.Options); /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel { Guard.NotNull(options, nameof(options)); diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs index dff9155401..86d798d965 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs @@ -33,12 +33,12 @@ public class WuQuantizer : IQuantizer public QuantizerOptions Options { get; } /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel => this.CreatePixelSpecificQuantizer(configuration, this.Options); /// - public IQuantizer CreatePixelSpecificQuantizer(Configuration? configuration, QuantizerOptions options) + public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel => new WuQuantizer(configuration, options); } From 4a82d27303519c1da92f6432fcaef7bd8ef96603 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Mon, 6 Feb 2023 10:50:12 +0100 Subject: [PATCH 38/47] Check iccProfileData for null not the profile --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index eaf6b4df7f..1479d31e64 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -228,9 +228,9 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals infoHeader.Compression = BmpCompression.BitFields; } - if (this.infoHeaderType is BmpInfoHeaderType.WinVersion5 && metadata.IccProfile != null) + if (this.infoHeaderType is BmpInfoHeaderType.WinVersion5 && iccProfileData != null) { - infoHeader.ProfileSize = iccProfileData!.Length; + infoHeader.ProfileSize = iccProfileData.Length; infoHeader.CsType = BmpColorSpace.PROFILE_EMBEDDED; infoHeader.Intent = BmpRenderingIntent.LCS_GM_IMAGES; } From 1c773661b4c19881cf844ebf19cf2ab3c93c1b1e Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Mon, 6 Feb 2023 10:54:56 +0100 Subject: [PATCH 39/47] Make method static --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 1479d31e64..620e0499f4 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -473,7 +473,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals using IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); for (int y = image.Height - 1; y >= 0; y--) { @@ -547,7 +547,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding; @@ -595,7 +595,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; @@ -652,7 +652,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); ReadOnlySpan quantizedPixelRow = quantized.DangerousGetRowSpan(0); int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding; @@ -688,7 +688,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The color palette from the quantized image. /// A temporary byte span to write the color palette to. /// The global configuration - private void WriteColorPalette(Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette, Configuration configuration) + private static void WriteColorPalette(Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette, Configuration configuration) where TPixel : unmanaged, IPixel { int quantizedColorBytes = quantizedColorPalette.Length * 4; From 88ddf709c57bf72d40ee04777ebb9f15e003d5c9 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 6 Feb 2023 20:02:11 +0100 Subject: [PATCH 40/47] Actually call arm methods, if arm is supported --- src/ImageSharp/Compression/Zlib/Adler32.cs | 5 +++++ src/ImageSharp/Compression/Zlib/Crc32.cs | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/ImageSharp/Compression/Zlib/Adler32.cs b/src/ImageSharp/Compression/Zlib/Adler32.cs index 1b1a77715d..dd8217541a 100644 --- a/src/ImageSharp/Compression/Zlib/Adler32.cs +++ b/src/ImageSharp/Compression/Zlib/Adler32.cs @@ -71,6 +71,11 @@ internal static class Adler32 return CalculateSse(adler, buffer); } + if (AdvSimd.IsSupported) + { + return CalculateArm(adler, buffer); + } + return CalculateScalar(adler, buffer); } diff --git a/src/ImageSharp/Compression/Zlib/Crc32.cs b/src/ImageSharp/Compression/Zlib/Crc32.cs index c85d58df5b..39c535c773 100644 --- a/src/ImageSharp/Compression/Zlib/Crc32.cs +++ b/src/ImageSharp/Compression/Zlib/Crc32.cs @@ -61,6 +61,16 @@ internal static partial class Crc32 return ~CalculateSse(~crc, buffer); } + if (ArmCrc32.Arm64.IsSupported) + { + return ~CalculateArm64(~crc, buffer); + } + + if (ArmCrc32.IsSupported) + { + return ~CalculateArm(~crc, buffer); + } + return ~CalculateScalar(~crc, buffer); } From 66d494e2505454320ab6052966eb91366f8ca09d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 Feb 2023 09:17:16 +1000 Subject: [PATCH 41/47] Reorder params, fix other warnings. --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 68 +++++++++---------- .../Quantization/OctreeQuantizer{TPixel}.cs | 7 +- .../Quantization/PaletteQuantizer{TPixel}.cs | 11 ++- .../Quantization/WuQuantizer{TPixel}.cs | 8 +-- 4 files changed, 44 insertions(+), 50 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 620e0499f4..f30369d19b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -159,7 +159,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); - this.WriteImage(stream, image, configuration); + this.WriteImage(configuration, stream, image); WriteColorProfile(stream, iccProfileData, buffer); stream.Flush(); @@ -307,43 +307,43 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes the pixel data to the binary stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// /// The containing pixel data. /// - /// The global configuration. - private void WriteImage(Stream stream, Image image, Configuration configuration) + private void WriteImage(Configuration configuration, Stream stream, Image image) where TPixel : unmanaged, IPixel { Buffer2D pixels = image.Frames.RootFrame.PixelBuffer; switch (this.bitsPerPixel) { case BmpBitsPerPixel.Pixel32: - this.Write32BitPixelData(stream, pixels, configuration); + this.Write32BitPixelData(configuration, stream, pixels); break; case BmpBitsPerPixel.Pixel24: - this.Write24BitPixelData(stream, pixels, configuration); + this.Write24BitPixelData(configuration, stream, pixels); break; case BmpBitsPerPixel.Pixel16: - this.Write16BitPixelData(stream, pixels, configuration); + this.Write16BitPixelData(configuration, stream, pixels); break; case BmpBitsPerPixel.Pixel8: - this.Write8BitPixelData(stream, image, configuration); + this.Write8BitPixelData(configuration, stream, image); break; case BmpBitsPerPixel.Pixel4: - this.Write4BitPixelData(stream, image, configuration); + this.Write4BitPixelData(configuration, stream, image); break; case BmpBitsPerPixel.Pixel2: - this.Write2BitPixelData(stream, image, configuration); + this.Write2BitPixelData(configuration, stream, image); break; case BmpBitsPerPixel.Pixel1: - this.Write1BitPixelData(stream, image, configuration); + this.Write1BitPixelData(configuration, stream, image); break; } } @@ -355,10 +355,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes 32-bit data with a color palette to the stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// The containing pixel data. - /// The global configuration. - private void Write32BitPixelData(Stream stream, Buffer2D pixels, Configuration configuration) + private void Write32BitPixelData(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 4); @@ -380,10 +380,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes 24-bit pixel data with a color palette to the stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// The containing pixel data. - /// The global configuration. - private void Write24BitPixelData(Stream stream, Buffer2D pixels, Configuration configuration) + private void Write24BitPixelData(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -407,11 +407,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes 16-bit pixel data with a color palette to the stream. /// /// The type of the pixel. + /// The global configuration. /// The to write to. /// The containing pixel data. - /// The global configuration. - private void Write16BitPixelData(Stream stream, Buffer2D pixels, Configuration configuration) - where TPixel : unmanaged, IPixel + private void Write16BitPixelData(Configuration configuration, Stream stream, Buffer2D pixels) + where TPixel : unmanaged, IPixel { int width = pixels.Width; int rowBytesWithoutPadding = width * 2; @@ -436,10 +436,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes 8 bit pixel data with a color palette. The color palette has 256 entry's with 4 bytes for each entry. /// /// The type of the pixel. + /// The global configuration. /// The to write to. /// The containing pixel data. - /// The global configuration. - private void Write8BitPixelData(Stream stream, Image image, Configuration configuration) + private void Write8BitPixelData(Configuration configuration, Stream stream, Image image) where TPixel : unmanaged, IPixel { bool isL8 = typeof(TPixel) == typeof(L8); @@ -452,7 +452,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals } else { - this.Write8BitColor(stream, image, colorPalette, configuration); + this.Write8BitColor(configuration, stream, image, colorPalette); } } @@ -460,11 +460,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes an 8 bit color image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. /// /// The type of the pixel. + /// The global configuration. /// The to write to. /// The containing pixel data. /// A byte span of size 1024 for the color palette. - /// The global configuration - private void Write8BitColor(Stream stream, Image image, Span colorPalette, Configuration configuration) + private void Write8BitColor(Configuration configuration, Stream stream, Image image, Span colorPalette) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration); @@ -473,7 +473,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals using IndexedImageFrame quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); for (int y = image.Height - 1; y >= 0; y--) { @@ -529,10 +529,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes 4 bit pixel data with a color palette. The color palette has 16 entry's with 4 bytes for each entry. /// /// The type of the pixel. + /// The global configuration. /// The to write to. /// The containing pixel data. - /// The global configuration. - private void Write4BitPixelData(Stream stream, Image image, Configuration configuration) + private void Write4BitPixelData(Configuration configuration, Stream stream, Image image) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions() @@ -547,7 +547,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding; @@ -577,10 +577,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes 2 bit pixel data with a color palette. The color palette has 4 entry's with 4 bytes for each entry. /// /// The type of the pixel. + /// The global configuration. /// The to write to. /// The containing pixel data. - /// The global configuration - private void Write2BitPixelData(Stream stream, Image image, Configuration configuration) + private void Write2BitPixelData(Configuration configuration, Stream stream, Image image) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions() @@ -595,7 +595,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; @@ -634,10 +634,10 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes 1 bit pixel data with a color palette. The color palette has 2 entry's with 4 bytes for each entry. /// /// The type of the pixel. + /// The global configuration. /// The to write to. /// The containing pixel data. - /// The global configuration - private void Write1BitPixelData(Stream stream, Image image, Configuration configuration) + private void Write1BitPixelData(Configuration configuration, Stream stream, Image image) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions() @@ -652,7 +652,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - WriteColorPalette(stream, quantizedColorPalette, colorPalette, configuration); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); ReadOnlySpan quantizedPixelRow = quantized.DangerousGetRowSpan(0); int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding; @@ -684,11 +684,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// Writes the color palette to the stream. The color palette has 4 bytes for each entry. /// /// The type of the pixel. + /// The global configuration. /// The to write to. /// The color palette from the quantized image. /// A temporary byte span to write the color palette to. - /// The global configuration - private static void WriteColorPalette(Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette, Configuration configuration) + private static void WriteColorPalette(Configuration configuration, Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette) where TPixel : unmanaged, IPixel { int quantizedColorBytes = quantizedColorPalette.Length * 4; diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs index 37ebc39bac..1136fbc9da 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs @@ -26,7 +26,7 @@ public struct OctreeQuantizer : IQuantizer private readonly int maxColors; private readonly int bitDepth; private readonly Octree octree; - private IMemoryOwner paletteOwner; + private readonly IMemoryOwner paletteOwner; private ReadOnlyMemory palette; private EuclideanPixelMap? pixelMap; private readonly bool isDithering; @@ -38,11 +38,8 @@ public struct OctreeQuantizer : IQuantizer /// The configuration which allows altering default behaviour or extending the library. /// The quantizer options defining quantization rules. [MethodImpl(InliningOptions.ShortMethod)] - public OctreeQuantizer(Configuration? configuration, QuantizerOptions options) + public OctreeQuantizer(Configuration configuration, QuantizerOptions options) { - Guard.NotNull(configuration, nameof(configuration)); - Guard.NotNull(options, nameof(options)); - this.Configuration = configuration; this.Options = options; diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs index 182bed2382..86db9f6f01 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs @@ -17,10 +17,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization; "Design", "CA1001:Types that own disposable fields should be disposable", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/6151")] -internal struct PaletteQuantizer : IQuantizer +internal readonly struct PaletteQuantizer : IQuantizer where TPixel : unmanaged, IPixel { - private EuclideanPixelMap pixelMap; + private readonly EuclideanPixelMap pixelMap; /// /// Initializes a new instance of the struct. @@ -29,7 +29,7 @@ internal struct PaletteQuantizer : IQuantizer /// The quantizer options defining quantization rules. /// The palette to use. [MethodImpl(InliningOptions.ShortMethod)] - public PaletteQuantizer(Configuration? configuration, QuantizerOptions options, ReadOnlyMemory palette) + public PaletteQuantizer(Configuration configuration, QuantizerOptions options, ReadOnlyMemory palette) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(options, nameof(options)); @@ -65,8 +65,5 @@ internal struct PaletteQuantizer : IQuantizer => (byte)this.pixelMap.GetClosestColor(color, out match); /// - public void Dispose() - { - this.pixelMap.Dispose(); - } + public void Dispose() => this.pixelMap.Dispose(); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs index c04dbe1918..edf293d39e 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs @@ -69,9 +69,9 @@ internal struct WuQuantizer : IQuantizer /// private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; - private IMemoryOwner momentsOwner; - private IMemoryOwner tagsOwner; - private IMemoryOwner paletteOwner; + private readonly IMemoryOwner momentsOwner; + private readonly IMemoryOwner tagsOwner; + private readonly IMemoryOwner paletteOwner; private ReadOnlyMemory palette; private int maxColors; private readonly Box[] colorCube; @@ -85,7 +85,7 @@ internal struct WuQuantizer : IQuantizer /// The configuration which allows altering default behaviour or extending the library. /// The quantizer options defining quantization rules. [MethodImpl(InliningOptions.ShortMethod)] - public WuQuantizer(Configuration? configuration, QuantizerOptions options) + public WuQuantizer(Configuration configuration, QuantizerOptions options) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(options, nameof(options)); From c6e583991c1916b7f39adf45da20a1510cd31e35 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 Feb 2023 21:56:08 +1000 Subject: [PATCH 42/47] Minor cleanup --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 8 ++++---- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 4 +--- src/ImageSharp/Image.cs | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 6c82d61e1d..3ebd65a67f 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -337,9 +337,9 @@ internal sealed class GifDecoderCore : IImageDecoderInternals /// /// Skips over a block or reads its terminator. - /// The length of the block to skip. - /// The containing image data. /// + /// The containing image data. + /// The length of the block to skip. private static void SkipBlock(BufferedReadStream stream, int blockSize = 0) { if (blockSize > 0) @@ -363,7 +363,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { int length; - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new(); while ((length = stream.ReadByte()) != 0) { if (length > GifConstants.MaxCommentSubBlockLength) @@ -450,7 +450,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals private void ReadFrameIndices(BufferedReadStream stream, Buffer2D indices) { int minCodeSize = stream.ReadByte(); - using LzwDecoder lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, stream); + using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream); lzwDecoder.DecodePixels(minCodeSize, indices); } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 0e59a0f05e..75c9b11860 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; @@ -141,7 +140,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals private void EncodeFrames( Stream stream, Image image, - IndexedImageFrame? quantized, + IndexedImageFrame quantized, ReadOnlyMemory palette) where TPixel : unmanaged, IPixel { @@ -168,7 +167,6 @@ internal sealed class GifEncoderCore : IImageEncoderInternals // Clean up for the next run. quantized.Dispose(); - quantized = null; } if (hasPaletteQuantizer) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index af1b1916f9..02fa014781 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -30,7 +30,7 @@ public abstract partial class Image : ImageInfo, IDisposable, IConfigurationProv /// The size in px units. protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata? metadata, Size size) : base(pixelType, size, metadata) - => this.configuration = configuration ?? Configuration.Default; + => this.configuration = configuration; /// /// Initializes a new instance of the class. From b560918106aa378b633ec0746e7dc1814ce9ac88 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 8 Feb 2023 18:10:11 +0100 Subject: [PATCH 43/47] do not use null!. --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 65ab8dfc15..f04ba25ff3 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -29,12 +29,12 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals /// /// The metadata. /// - private ImageMetadata metadata = null!; + private ImageMetadata? metadata; /// /// The tga specific metadata. /// - private TgaMetadata tgaMetadata = null!; + private TgaMetadata? tgaMetadata; /// /// The file header containing general information about the image. @@ -533,6 +533,9 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { TPixel color = default; bool invertX = InvertX(origin); + + Guard.NotNull(this.tgaMetadata); + if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX) { using IMemoryOwner row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0); @@ -591,6 +594,9 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals where TPixel : unmanaged, IPixel { TPixel color = default; + + Guard.NotNull(this.tgaMetadata); + byte alphaBits = this.tgaMetadata.AlphaChannelBits; using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) { @@ -722,6 +728,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel"); } + Guard.NotNull(this.tgaMetadata); + byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3]; color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha)); pixelRow[x] = color; From b79e61fbd9306d422c5611f5ac2c29cb9ec7efc5 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Wed, 8 Feb 2023 20:08:43 +0100 Subject: [PATCH 44/47] Remove nullable disable from ChunkedMemoryStream --- src/ImageSharp/IO/ChunkedMemoryStream.cs | 39 +++++++++++++----------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs index 5f388d24f1..2e088397a0 100644 --- a/src/ImageSharp/IO/ChunkedMemoryStream.cs +++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Runtime.CompilerServices; @@ -19,7 +18,7 @@ internal sealed class ChunkedMemoryStream : Stream private readonly MemoryAllocator allocator; // Data - private MemoryChunk memoryChunk; + private MemoryChunk? memoryChunk; // The total number of allocated chunks private int chunkCount; @@ -31,13 +30,13 @@ internal sealed class ChunkedMemoryStream : Stream private bool isDisposed; // Current chunk to write to - private MemoryChunk writeChunk; + private MemoryChunk? writeChunk; // Offset into chunk to write to private int writeOffset; // Current chunk to read from - private MemoryChunk readChunk; + private MemoryChunk? readChunk; // Offset into chunk to read from private int readOffset; @@ -71,10 +70,10 @@ internal sealed class ChunkedMemoryStream : Stream this.EnsureNotDisposed(); int length = 0; - MemoryChunk chunk = this.memoryChunk; + MemoryChunk? chunk = this.memoryChunk; while (chunk != null) { - MemoryChunk next = chunk.Next; + MemoryChunk? next = chunk.Next; if (next != null) { length += chunk.Length; @@ -104,8 +103,8 @@ internal sealed class ChunkedMemoryStream : Stream } int pos = 0; - MemoryChunk chunk = this.memoryChunk; - while (chunk != this.readChunk) + MemoryChunk? chunk = this.memoryChunk; + while (chunk != this.readChunk && chunk is not null) { pos += chunk.Length; chunk = chunk.Next; @@ -126,14 +125,14 @@ internal sealed class ChunkedMemoryStream : Stream } // Back up current position in case new position is out of range - MemoryChunk backupReadChunk = this.readChunk; + MemoryChunk? backupReadChunk = this.readChunk; int backupReadOffset = this.readOffset; this.readChunk = null; this.readOffset = 0; int leftUntilAtPos = (int)value; - MemoryChunk chunk = this.memoryChunk; + MemoryChunk? chunk = this.memoryChunk; while (chunk != null) { if ((leftUntilAtPos < chunk.Length) @@ -365,6 +364,8 @@ internal sealed class ChunkedMemoryStream : Stream this.writeOffset = 0; } + Guard.NotNull(this.writeChunk); + Span chunkBuffer = this.writeChunk.Buffer.GetSpan(); int chunkSize = this.writeChunk.Length; int count = buffer.Length; @@ -402,6 +403,8 @@ internal sealed class ChunkedMemoryStream : Stream this.writeOffset = 0; } + Guard.NotNull(this.writeChunk); + IMemoryOwner chunkBuffer = this.writeChunk.Buffer; int chunkSize = this.writeChunk.Length; @@ -426,7 +429,7 @@ internal sealed class ChunkedMemoryStream : Stream int length = (int)this.Length; // This will throw if stream is closed byte[] copy = new byte[this.Length]; - MemoryChunk backupReadChunk = this.readChunk; + MemoryChunk? backupReadChunk = this.readChunk; int backupReadOffset = this.readOffset; this.readChunk = this.memoryChunk; @@ -522,15 +525,14 @@ internal sealed class ChunkedMemoryStream : Stream // available contiguous buffer size. IMemoryOwner buffer = this.allocator.Allocate(Math.Min(this.allocatorCapacity, GetChunkSize(this.chunkCount++))); - return new MemoryChunk + return new MemoryChunk(buffer) { - Buffer = buffer, Next = null, Length = buffer.Length() }; } - private static void ReleaseMemoryChunks(MemoryChunk chunk) + private static void ReleaseMemoryChunks(MemoryChunk? chunk) { while (chunk != null) { @@ -555,11 +557,13 @@ internal sealed class ChunkedMemoryStream : Stream { private bool isDisposed; - public IMemoryOwner Buffer { get; set; } + public MemoryChunk(IMemoryOwner buffer) => this.Buffer = buffer; + + public IMemoryOwner Buffer { get; } - public MemoryChunk Next { get; set; } + public MemoryChunk? Next { get; set; } - public int Length { get; set; } + public int Length { get; init; } private void Dispose(bool disposing) { @@ -570,7 +574,6 @@ internal sealed class ChunkedMemoryStream : Stream this.Buffer.Dispose(); } - this.Buffer = null; this.isDisposed = true; } } From c8fd4a321dec6257f2fe8bf5881c62bfe30251e3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 9 Feb 2023 20:30:08 +1000 Subject: [PATCH 45/47] Match other format configuration handling --- src/ImageSharp/Formats/Tga/TgaEncoderCore.cs | 41 ++++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index 0b987a3d6c..f468ab9ae7 100644 --- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -22,11 +22,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// private readonly MemoryAllocator memoryAllocator; - /// - /// The global configuration. - /// - private Configuration? configuration; - /// /// Reusable buffer for writing data. /// @@ -67,7 +62,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - this.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; TgaMetadata tgaMetadata = metadata.GetTgaMetadata(); this.bitsPerPixel ??= tgaMetadata.BitsPerPixel; @@ -123,7 +117,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals } else { - this.WriteImage(stream, image.Frames.RootFrame); + this.WriteImage(image.GetConfiguration(), stream, image.Frames.RootFrame); } stream.Flush(); @@ -133,30 +127,31 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// Writes the pixel data to the binary stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// /// The containing pixel data. /// - private void WriteImage(Stream stream, ImageFrame image) + private void WriteImage(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { Buffer2D pixels = image.PixelBuffer; switch (this.bitsPerPixel) { case TgaBitsPerPixel.Pixel8: - this.Write8Bit(stream, pixels); + this.Write8Bit(configuration, stream, pixels); break; case TgaBitsPerPixel.Pixel16: - this.Write16Bit(stream, pixels); + this.Write16Bit(configuration, stream, pixels); break; case TgaBitsPerPixel.Pixel24: - this.Write24Bit(stream, pixels); + this.Write24Bit(configuration, stream, pixels); break; case TgaBitsPerPixel.Pixel32: - this.Write32Bit(stream, pixels); + this.Write32Bit(configuration, stream, pixels); break; } } @@ -226,7 +221,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals case TgaBitsPerPixel.Pixel16: Bgra5551 bgra5551 = new(color.ToVector4()); - BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); + BinaryPrimitives.WriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); stream.WriteByte(this.buffer[0]); stream.WriteByte(this.buffer[1]); @@ -320,9 +315,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// Writes the 8bit pixels uncompressed to the stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write8Bit(Stream stream, Buffer2D pixels) + private void Write8Bit(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 1); @@ -332,7 +328,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToL8Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -344,9 +340,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// Writes the 16bit pixels uncompressed to the stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write16Bit(Stream stream, Buffer2D pixels) + private void Write16Bit(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 2); @@ -356,7 +353,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra5551Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -368,9 +365,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// Writes the 24bit pixels uncompressed to the stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write24Bit(Stream stream, Buffer2D pixels) + private void Write24Bit(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 3); @@ -380,7 +378,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgr24Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -392,9 +390,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// Writes the 32bit pixels uncompressed to the stream. /// /// The pixel format. + /// The global configuration. /// The to write to. /// The containing pixel data. - private void Write32Bit(Stream stream, Buffer2D pixels) + private void Write32Bit(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 4); @@ -404,7 +403,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra32Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); From 829be7ac1bd4c3ff6143c8a5c831f21cc9531056 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 9 Feb 2023 20:31:44 +1000 Subject: [PATCH 46/47] Style cleanup --- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 155 +++++++++---------- 1 file changed, 75 insertions(+), 80 deletions(-) diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index f04ba25ff3..2428763432 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -307,39 +307,37 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals private void ReadPalettedRle(BufferedReadStream stream, int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { - using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean)) - { - TPixel color = default; - Span bufferSpan = buffer.GetSpan(); - this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel: 1); + using IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height, AllocationOptions.Clean); + TPixel color = default; + Span bufferSpan = buffer.GetSpan(); + this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel: 1); - for (int y = 0; y < height; y++) + for (int y = 0; y < height; y++) + { + int newY = InvertY(y, height, origin); + Span pixelRow = pixels.DangerousGetRowSpan(newY); + int rowStartIdx = y * width; + for (int x = 0; x < width; x++) { - int newY = InvertY(y, height, origin); - Span pixelRow = pixels.DangerousGetRowSpan(newY); - int rowStartIdx = y * width; - for (int x = 0; x < width; x++) + int idx = rowStartIdx + x; + switch (colorMapPixelSizeInBytes) { - int idx = rowStartIdx + x; - switch (colorMapPixelSizeInBytes) - { - case 1: - color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); - break; - case 2: - this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color); - break; - case 3: - color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); - break; - case 4: - color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); - break; - } - - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + case 1: + color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + break; + case 2: + this.ReadPalettedBgra16Pixel(palette, bufferSpan[idx], colorMapPixelSizeInBytes, ref color); + break; + case 3: + color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + break; + case 4: + color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + break; } + + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } } } @@ -598,60 +596,58 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Guard.NotNull(this.tgaMetadata); byte alphaBits = this.tgaMetadata.AlphaChannelBits; - using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) + using IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean); + Span bufferSpan = buffer.GetSpan(); + this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel); + for (int y = 0; y < height; y++) { - Span bufferSpan = buffer.GetSpan(); - this.UncompressRle(stream, width, height, bufferSpan, bytesPerPixel); - for (int y = 0; y < height; y++) + int newY = InvertY(y, height, origin); + Span pixelRow = pixels.DangerousGetRowSpan(newY); + int rowStartIdx = y * width * bytesPerPixel; + for (int x = 0; x < width; x++) { - int newY = InvertY(y, height, origin); - Span pixelRow = pixels.DangerousGetRowSpan(newY); - int rowStartIdx = y * width * bytesPerPixel; - for (int x = 0; x < width; x++) + int idx = rowStartIdx + (x * bytesPerPixel); + switch (bytesPerPixel) { - int idx = rowStartIdx + (x * bytesPerPixel); - switch (bytesPerPixel) - { - case 1: - color.FromL8(Unsafe.As(ref bufferSpan[idx])); - break; - case 2: - if (!this.hasAlpha) - { - // Set alpha value to 1, to treat it as opaque for Bgra5551. - bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128); - } - - if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite) - { - color.FromLa16(Unsafe.As(ref bufferSpan[idx])); - } - else - { - color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); - } - - break; - case 3: - color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); - break; - case 4: - if (this.hasAlpha) - { - color.FromBgra32(Unsafe.As(ref bufferSpan[idx])); - } - else - { - byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; - color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha)); - } - - break; - } + case 1: + color.FromL8(Unsafe.As(ref bufferSpan[idx])); + break; + case 2: + if (!this.hasAlpha) + { + // Set alpha value to 1, to treat it as opaque for Bgra5551. + bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128); + } - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite) + { + color.FromLa16(Unsafe.As(ref bufferSpan[idx])); + } + else + { + color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); + } + + break; + case 3: + color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); + break; + case 4: + if (this.hasAlpha) + { + color.FromBgra32(Unsafe.As(ref bufferSpan[idx])); + } + else + { + byte alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; + color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], alpha)); + } + + break; } + + int newX = InvertX(x, width, origin); + pixelRow[newX] = color; } } } @@ -952,7 +948,6 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals this.hasAlpha = alphaBits > 0; // Bits 4 and 5 describe the image origin. - TgaImageOrigin origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); - return origin; + return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); } } From ffd5c360a4e466b98d5dfb8198c45648ca060916 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 9 Feb 2023 20:57:58 +1000 Subject: [PATCH 47/47] Remove unneccessary guard --- src/ImageSharp/IO/ChunkedMemoryStream.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs index 2e088397a0..da52f7ca85 100644 --- a/src/ImageSharp/IO/ChunkedMemoryStream.cs +++ b/src/ImageSharp/IO/ChunkedMemoryStream.cs @@ -47,8 +47,6 @@ internal sealed class ChunkedMemoryStream : Stream /// The memory allocator. public ChunkedMemoryStream(MemoryAllocator allocator) { - Guard.NotNull(allocator, nameof(allocator)); - this.allocatorCapacity = allocator.GetBufferCapacityInBytes(); this.allocator = allocator; }