diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index c033dd133c..7c7504371d 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 @@ -32,6 +35,12 @@ jobs: sdk-preview: true runtime: -x64 codecov: false + - os: buildjet-4vcpu-ubuntu-2204-arm + 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,10 +56,23 @@ 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}} steps: + - name: Install libgdi+, which is required for tests running on ubuntu + 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 shell: bash run: | @@ -92,7 +114,6 @@ jobs: if: ${{ matrix.options.sdk-preview != true }} uses: actions/setup-dotnet@v3 with: - include-prerelease: true dotnet-version: | 6.0.x @@ -100,7 +121,6 @@ jobs: if: ${{ matrix.options.sdk-preview == true }} uses: actions/setup-dotnet@v3 with: - include-prerelease: true dotnet-version: | 7.0.x diff --git a/ci-pack.ps1 b/ci-pack.ps1 index 09f45347ef..55c69fb590 100644 --- a/ci-pack.ps1 +++ b/ci-pack.ps1 @@ -3,4 +3,4 @@ dotnet clean -c Release $repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" # Building for packing and publishing. -dotnet pack -c Release --output "$PSScriptRoot/artifacts" /p:RepositoryUrl=$repositoryUrl +dotnet pack -c Release -p:PackageOutputPath="$PSScriptRoot/artifacts" -p:RepositoryUrl=$repositoryUrl 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 } 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/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 99a271bcf7..13af25f6c7 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -286,13 +286,10 @@ 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) -#pragma warning restore RCS1163 // Unused parameter. + public static void ToPixel(ReadOnlySpan source, Span destination) where TPixel : unmanaged, IPixel { // TODO: Investigate bulk operations utilizing configuration parameter here. diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index fc6cfd585a..81cc4b5399 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.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; @@ -808,6 +809,25 @@ internal static class Numerics return Sse2.ConvertToInt32(vsum); } + /// + /// Reduces elements of the vector into one sum. + /// + /// The accumulator to reduce. + /// The sum of all elements. + [MethodImpl(InliningOptions.ShortMethod)] + public static int ReduceSumArm(Vector128 accumulator) + { + if (AdvSimd.Arm64.IsSupported) + { + Vector64 sum = AdvSimd.Arm64.AddAcross(accumulator); + return (int)AdvSimd.Extract(sum, 0); + } + + Vector128 sum2 = AdvSimd.AddPairwiseWidening(accumulator); + Vector64 sum3 = AdvSimd.Add(sum2.GetLower().AsUInt32(), sum2.GetUpper().AsUInt32()); + return (int)AdvSimd.Extract(sum3, 0); + } + /// /// Reduces even elements of the vector into one sum. /// diff --git a/src/ImageSharp/Compression/Zlib/Adler32.cs b/src/ImageSharp/Compression/Zlib/Adler32.cs index 3885ef5755..dd8217541a 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 @@ -70,6 +71,11 @@ internal static class Adler32 return CalculateSse(adler, buffer); } + if (AdvSimd.IsSupported) + { + return CalculateArm(adler, buffer); + } + return CalculateScalar(adler, buffer); } @@ -95,7 +101,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 +185,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 +249,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 +386,6 @@ internal static class Adler32 { uint s1 = adler & 0xFFFF; uint s2 = (adler >> 16) & 0xFFFF; - uint k; fixed (byte* bufferPtr = buffer) { @@ -295,7 +394,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..39c535c773 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; @@ -60,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); } @@ -187,6 +198,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/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..f30369d19b 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; @@ -69,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. /// @@ -124,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; @@ -142,7 +136,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals _ => 0 }; - byte[] iccProfileData = null; + byte[]? iccProfileData = null; int iccProfileSize = 0; if (metadata.IccProfile != null) { @@ -165,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(configuration, stream, image); WriteColorProfile(stream, iccProfileData, buffer); stream.Flush(); @@ -182,7 +176,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; @@ -234,7 +228,7 @@ 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.CsType = BmpColorSpace.PROFILE_EMBEDDED; @@ -250,7 +244,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) { @@ -313,42 +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. /// - private void WriteImage(Stream stream, Image image) + 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); + this.Write32BitPixelData(configuration, stream, pixels); break; case BmpBitsPerPixel.Pixel24: - this.Write24BitPixelData(stream, pixels); + this.Write24BitPixelData(configuration, stream, pixels); break; case BmpBitsPerPixel.Pixel16: - this.Write16BitPixelData(stream, pixels); + this.Write16BitPixelData(configuration, stream, pixels); break; case BmpBitsPerPixel.Pixel8: - this.Write8BitPixelData(stream, image); + this.Write8BitPixelData(configuration, stream, image); break; case BmpBitsPerPixel.Pixel4: - this.Write4BitPixelData(stream, image); + this.Write4BitPixelData(configuration, stream, image); break; case BmpBitsPerPixel.Pixel2: - this.Write2BitPixelData(stream, image); + this.Write2BitPixelData(configuration, stream, image); break; case BmpBitsPerPixel.Pixel1: - this.Write1BitPixelData(stream, image); + this.Write1BitPixelData(configuration, stream, image); break; } } @@ -360,9 +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. - private void Write32BitPixelData(Stream stream, Buffer2D pixels) + private void Write32BitPixelData(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 4); @@ -372,7 +368,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra32Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -384,9 +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. - private void Write24BitPixelData(Stream stream, Buffer2D pixels) + private void Write24BitPixelData(Configuration configuration, Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -398,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); @@ -410,10 +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. - private void Write16BitPixelData(Stream stream, Buffer2D pixels) - 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; @@ -425,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); @@ -438,9 +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. - private void Write8BitPixelData(Stream stream, Image image) + private void Write8BitPixelData(Configuration configuration, Stream stream, Image image) where TPixel : unmanaged, IPixel { bool isL8 = typeof(TPixel) == typeof(L8); @@ -453,7 +452,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals } else { - this.Write8BitColor(stream, image, colorPalette); + this.Write8BitColor(configuration, stream, image, colorPalette); } } @@ -461,19 +460,20 @@ 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. - private void Write8BitColor(Stream stream, Image image, Span colorPalette) + private void Write8BitColor(Configuration configuration, Stream stream, Image image, Span colorPalette) 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); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); for (int y = image.Height - 1; y >= 0; y--) { @@ -529,12 +529,13 @@ 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. - private void Write4BitPixelData(Stream stream, Image image) + private void Write4BitPixelData(Configuration configuration, Stream stream, Image image) 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 }); @@ -546,7 +547,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 2 != 0 ? this.padding - 1 : this.padding; @@ -576,12 +577,13 @@ 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. - private void Write2BitPixelData(Stream stream, Image image) + private void Write2BitPixelData(Configuration configuration, Stream stream, Image image) 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 }); @@ -593,7 +595,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; @@ -632,12 +634,13 @@ 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. - private void Write1BitPixelData(Stream stream, Image image) + private void Write1BitPixelData(Configuration configuration, Stream stream, Image image) 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 }); @@ -649,7 +652,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Span colorPalette = colorPaletteBuffer.GetSpan(); ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; - this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + WriteColorPalette(configuration, stream, quantizedColorPalette, colorPalette); ReadOnlySpan quantizedPixelRow = quantized.DangerousGetRowSpan(0); int rowPadding = quantizedPixelRow.Length % 8 != 0 ? this.padding - 1 : this.padding; @@ -681,14 +684,15 @@ 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. - private void WriteColorPalette(Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette) + private static void WriteColorPalette(Configuration configuration, Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette) 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/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 989fc49fc2..6243a071d5 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -15,15 +15,25 @@ public sealed class DecoderOptions private uint maxFrames = int.MaxValue; + // Used by the FileProvider in the unit tests to set the configuration on the fly. +#pragma warning disable IDE0032 // Use auto property + private Configuration configuration = Configuration.Default; +#pragma warning restore IDE0032 // Use auto property + /// /// Gets the shared default general decoder options instance. + /// Used internally to reduce allocations for default decoding operations. /// internal static DecoderOptions Default { get; } = LazyOptions.Value; /// /// Gets a custom configuration instance to be used by the image processing pipeline. /// - public Configuration Configuration { get; internal set; } = Configuration.Default; +#pragma warning disable IDE0032 // Use auto property +#pragma warning disable RCS1085 // Use auto-implemented property. + public Configuration Configuration { get => this.configuration; init => this.configuration = value; } +#pragma warning restore RCS1085 // Use auto-implemented property. +#pragma warning restore IDE0032 // Use auto property /// /// Gets the target size to decode the image into. Scaling should use an operation equivalent to . @@ -44,4 +54,6 @@ public sealed class DecoderOptions /// Gets the maximum number of image frames to decode, inclusive. /// public uint MaxFrames { get => this.maxFrames; init => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } + + internal void SetConfiguration(Configuration configuration) => this.configuration = configuration; } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 8c59158000..3ebd65a67f 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. + 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 + 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. + 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; + 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); + SkipBlock(stream, subBlockSize); return; } - this.SkipBlock(appLength); // Not supported by any known decoder. + SkipBlock(stream, appLength); // Not supported by any known decoder. } /// /// Skips over a block or reads its terminator. - /// The length of the block to skip. /// - private void SkipBlock(int blockSize = 0) + /// The containing image data. + /// The length of the block to skip. + private static 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) + StringBuilder stringBuilder = new(); + 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(); + 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(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 9cc045d45e..75c9b11860 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.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; @@ -93,7 +92,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 +128,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); } @@ -152,8 +151,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,14 +163,16 @@ 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(); - quantized = null; } - paletteQuantizer.Dispose(); + if (hasPaletteQuantizer) + { + paletteQuantizer.Dispose(); + } } private void EncodeFrame( @@ -179,7 +180,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 @@ -190,7 +191,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) { @@ -335,7 +336,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) @@ -347,7 +348,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); } } @@ -436,7 +437,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/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/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. 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/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 9c32471fd2..2428763432 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; /// /// The tga specific metadata. /// - private TgaMetadata tgaMetadata; + private TgaMetadata? tgaMetadata; /// /// 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,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } else { for (int x = 0; x < width; x++) { - this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgr24Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -279,14 +277,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals { for (int x = width - 1; x >= 0; x--) { - this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } else { for (int x = 0; x < width; x++) { - this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow); + ReadPalettedBgra32Pixel(stream, palette, colorMapPixelSizeInBytes, x, color, pixelRow); } } @@ -299,48 +297,47 @@ 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); + 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; } } } @@ -349,11 +346,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 +364,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals Span pixelSpan = pixels.DangerousGetRowSpan(newY); for (int x = width - 1; x >= 0; x--) { - this.ReadL8Pixel(color, x, pixelSpan); + ReadL8Pixel(stream, color, x, pixelSpan); } } @@ -380,14 +378,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 +394,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 +416,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 +441,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 +472,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 +490,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 +505,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,15 +521,19 @@ 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; bool invertX = InvertX(origin); + + Guard.NotNull(this.tgaMetadata); + if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX) { using IMemoryOwner row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0); @@ -539,14 +543,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 +565,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,70 +582,72 @@ 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; + + 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(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); + } + + if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite) + { + color.FromLa16(Unsafe.As(ref bufferSpan[idx])); + } + else + { + color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); + } - int newX = InvertX(x, width, origin); - pixelRow[newX] = color; + 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; } } } @@ -658,10 +664,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 +678,19 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadL8Pixel(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)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 +701,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,25 +715,27 @@ 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"); } + 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; } [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 +746,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 +776,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgr24Pixel(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 = this.currentStream.ReadByte(); + int colorIndex = stream.ReadByte(); if (colorIndex == -1) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); @@ -782,10 +790,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadPalettedBgra32Pixel(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 = this.currentStream.ReadByte(); + int colorIndex = stream.ReadByte(); if (colorIndex == -1) { TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read color index"); @@ -798,25 +806,26 @@ 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. /// 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 +845,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 +926,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 +948,6 @@ 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); - return origin; + return (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); } } diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs index 44799c894b..f468ab9ae7 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; @@ -23,11 +22,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals /// private readonly MemoryAllocator memoryAllocator; - /// - /// The global configuration. - /// - private Configuration configuration; - /// /// Reusable buffer for writing data. /// @@ -68,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; @@ -124,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(); @@ -134,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; } } @@ -227,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]); @@ -321,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); @@ -333,7 +328,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToL8Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -345,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); @@ -357,7 +353,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra5551Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -369,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); @@ -381,7 +378,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgr24Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); @@ -393,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); @@ -405,7 +403,7 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals { Span pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.ToBgra32Bytes( - this.configuration, + configuration, pixelSpan, rowSpan, pixelSpan.Length); diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs index be1d00e7ed..9a4e4ba2b9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs @@ -67,6 +67,11 @@ internal static class HorizontalPredictor { ApplyHorizontalPrediction8Bit(rows, width); } + else if (bitsPerPixel == 16) + { + // Assume rows are L16 grayscale since that's currently the only way 16 bits is supported by encoder + ApplyHorizontalPrediction16Bit(rows, width); + } else if (bitsPerPixel == 24) { ApplyHorizontalPrediction24Bit(rows, width); @@ -102,6 +107,32 @@ internal static class HorizontalPredictor } } + /// + /// Applies a horizontal predictor to the L16 row. + /// Make use of the fact that many continuous-tone images rarely vary much in pixel value from one pixel to the next. + /// In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus + /// or minus 1, and so on.This reduces the apparent information content and allows LZW to encode the data more compactly. + /// + /// The L16 pixel rows. + /// The width. + [MethodImpl(InliningOptions.ShortMethod)] + private static void ApplyHorizontalPrediction16Bit(Span rows, int width) + { + DebugGuard.IsTrue(rows.Length % width == 0, "Values must be equals"); + int height = rows.Length / width; + for (int y = 0; y < height; y++) + { + Span rowSpan = rows.Slice(y * width, width); + Span rowL16 = MemoryMarshal.Cast(rowSpan); + + for (int x = rowL16.Length - 1; x >= 1; x--) + { + ushort val = (ushort)(rowL16[x].PackedValue - rowL16[x - 1].PackedValue); + rowL16[x].PackedValue = val; + } + } + } + /// /// Applies a horizontal predictor to a gray pixel row. /// diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs index 05dacfef65..978860910c 100644 --- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs +++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs @@ -73,6 +73,11 @@ internal static class TiffConstants /// public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0); + /// + /// The bits per sample for 16-bit grayscale images. + /// + public static readonly TiffBitsPerSample BitsPerSample16Bit = new TiffBitsPerSample(16, 0, 0); + /// /// The bits per sample for color images with 8 bits for each color channel. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index bcc249bbec..38da4b5f8b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -54,7 +54,7 @@ public enum TiffBitsPerPixel /// /// 16 bits per pixel, for gray images. /// - /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 24 bits per pixel instead. + /// Note: The TiffEncoder does not yet support 16 bits per color channel and will default to 16 bits grayscale instead. /// Bit16 = 16, diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 3a4e71d3e8..94c7fb2b1e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -376,11 +376,14 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals case TiffBitsPerPixel.Bit8: this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor); break; + case TiffBitsPerPixel.Bit16: + // Assume desire to encode as L16 grayscale + this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); + break; case TiffBitsPerPixel.Bit6: case TiffBitsPerPixel.Bit10: case TiffBitsPerPixel.Bit12: case TiffBitsPerPixel.Bit14: - case TiffBitsPerPixel.Bit16: case TiffBitsPerPixel.Bit30: case TiffBitsPerPixel.Bit36: case TiffBitsPerPixel.Bit42: @@ -413,13 +416,20 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals return; } - // At the moment only 8 and 32 bits per pixel can be preserved by the tiff encoder. + // At the moment only 8, 16 and 32 bits per pixel can be preserved by the tiff encoder. if (inputBitsPerPixel == 8) { this.SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); return; } + if (inputBitsPerPixel == 16) + { + // Assume desire to encode as L16 grayscale + this.SetEncoderOptions(TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); + return; + } + this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); return; } @@ -434,6 +444,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals return; } + if (inputBitsPerPixel == 16) + { + this.SetEncoderOptions(TiffBitsPerPixel.Bit16, photometricInterpretation, compression, predictor); + return; + } + this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); return; diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 7dd073936f..cf9b4ae213 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -343,20 +343,20 @@ internal class TiffEncoderEntriesCollector return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); case TiffPhotometricInterpretation.WhiteIsZero: - if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) + return encoder.BitsPerPixel switch { - return TiffConstants.BitsPerSample1Bit.ToArray(); - } - - return TiffConstants.BitsPerSample8Bit.ToArray(); + TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(), + TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(), + _ => TiffConstants.BitsPerSample8Bit.ToArray() + }; case TiffPhotometricInterpretation.BlackIsZero: - if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) + return encoder.BitsPerPixel switch { - return TiffConstants.BitsPerSample1Bit.ToArray(); - } - - return TiffConstants.BitsPerSample8Bit.ToArray(); + TiffBitsPerPixel.Bit1 => TiffConstants.BitsPerSample1Bit.ToArray(), + TiffBitsPerPixel.Bit16 => TiffConstants.BitsPerSample16Bit.ToArray(), + _ => TiffConstants.BitsPerSample8Bit.ToArray() + }; default: return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs index a52d49a353..96c8aeb324 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs @@ -27,12 +27,13 @@ internal static class TiffColorWriterFactory return new TiffPaletteWriter(image, quantizer, pixelSamplingStrategy, memoryAllocator, configuration, entriesCollector, bitsPerPixel); case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.WhiteIsZero: - if (bitsPerPixel == 1) + return bitsPerPixel switch { - return new TiffBiColorWriter(image, memoryAllocator, configuration, entriesCollector); - } + 1 => new TiffBiColorWriter(image, memoryAllocator, configuration, entriesCollector), + 16 => new TiffGrayL16Writer(image, memoryAllocator, configuration, entriesCollector), + _ => new TiffGrayWriter(image, memoryAllocator, configuration, entriesCollector) + }; - return new TiffGrayWriter(image, memoryAllocator, configuration, entriesCollector); default: return new TiffRgbWriter(image, memoryAllocator, configuration, entriesCollector); } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayL16Writer{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayL16Writer{TPixel}.cs new file mode 100644 index 0000000000..3e0e074e95 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayL16Writer{TPixel}.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.Writers; + +internal sealed class TiffGrayL16Writer : TiffCompositeColorWriter + where TPixel : unmanaged, IPixel +{ + public TiffGrayL16Writer(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(image, memoryAllocator, configuration, entriesCollector) + { + } + + /// + public override int BitsPerPixel => 16; + + /// + protected override void EncodePixels(Span pixels, Span buffer) => PixelOperations.Instance.ToL16Bytes(this.Configuration, pixels, buffer, pixels.Length); +} diff --git a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs index 4756dea867..316c705e39 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs @@ -5,6 +5,7 @@ using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; // ReSharper disable InconsistentNaming @@ -14,7 +15,7 @@ internal static class LossyUtils { // Note: method name in libwebp reference implementation is called VP8SSE16x16. [MethodImpl(InliningOptions.ShortMethod)] - public static int Vp8_Sse16X16(Span a, Span b) + public static int Vp8_Sse16x16(Span a, Span b) { if (Avx2.IsSupported) { @@ -26,12 +27,17 @@ internal static class LossyUtils return Vp8_Sse16xN_Sse2(a, b, 8); } + if (AdvSimd.IsSupported) + { + return Vp8_Sse16x16_Neon(a, b); + } + return Vp8_SseNxN(a, b, 16, 16); } // Note: method name in libwebp reference implementation is called VP8SSE16x8. [MethodImpl(InliningOptions.ShortMethod)] - public static int Vp8_Sse16X8(Span a, Span b) + public static int Vp8_Sse16x8(Span a, Span b) { if (Avx2.IsSupported) { @@ -43,12 +49,17 @@ internal static class LossyUtils return Vp8_Sse16xN_Sse2(a, b, 4); } + if (AdvSimd.IsSupported) + { + return Vp8_Sse16x8_Neon(a, b); + } + return Vp8_SseNxN(a, b, 16, 8); } // Note: method name in libwebp reference implementation is called VP8SSE4x4. [MethodImpl(InliningOptions.ShortMethod)] - public static int Vp8_Sse4X4(Span a, Span b) + public static int Vp8_Sse4x4(Span a, Span b) { if (Avx2.IsSupported) { @@ -77,8 +88,8 @@ internal static class LossyUtils Vector256 b01s = Avx2.UnpackLow(b01.AsByte(), Vector256.Zero); // subtract, square and accumulate. - Vector256 d0 = Avx2.SubtractSaturate(a01s, b01s); - Vector256 e0 = Avx2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16()); + Vector256 d0 = Avx2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16()); + Vector256 e0 = Avx2.MultiplyAddAdjacent(d0, d0); return Numerics.ReduceSum(e0); } @@ -110,15 +121,20 @@ internal static class LossyUtils Vector128 b23s = Sse2.UnpackLow(b23.AsByte(), Vector128.Zero); // subtract, square and accumulate. - Vector128 d0 = Sse2.SubtractSaturate(a01s, b01s); - Vector128 d1 = Sse2.SubtractSaturate(a23s, b23s); - Vector128 e0 = Sse2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16()); - Vector128 e1 = Sse2.MultiplyAddAdjacent(d1.AsInt16(), d1.AsInt16()); + Vector128 d0 = Sse2.SubtractSaturate(a01s.AsInt16(), b01s.AsInt16()); + Vector128 d1 = Sse2.SubtractSaturate(a23s.AsInt16(), b23s.AsInt16()); + Vector128 e0 = Sse2.MultiplyAddAdjacent(d0, d0); + Vector128 e1 = Sse2.MultiplyAddAdjacent(d1, d1); Vector128 sum = Sse2.Add(e0, e1); return Numerics.ReduceSum(sum); } + if (AdvSimd.IsSupported) + { + return Vp8_Sse4x4_Neon(a, b); + } + return Vp8_SseNxN(a, b, 4, 4); } @@ -126,18 +142,16 @@ internal static class LossyUtils public static int Vp8_SseNxN(Span a, Span b, int w, int h) { int count = 0; - int aOffset = 0; - int bOffset = 0; + int offset = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - int diff = a[aOffset + x] - b[bOffset + x]; + int diff = a[offset + x] - b[offset + x]; count += diff * diff; } - aOffset += WebpConstants.Bps; - bOffset += WebpConstants.Bps; + offset += WebpConstants.Bps; } return count; @@ -201,6 +215,106 @@ internal static class LossyUtils return Numerics.ReduceSum(sum); } + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe int Vp8_Sse16x16_Neon(Span a, Span b) + { + Vector128 sum = Vector128.Zero; + fixed (byte* aRef = &MemoryMarshal.GetReference(a)) + { + fixed (byte* bRef = &MemoryMarshal.GetReference(b)) + { + for (int y = 0; y < 16; y++) + { + sum = AccumulateSSE16Neon(aRef + (y * WebpConstants.Bps), bRef + (y * WebpConstants.Bps), sum); + } + } + } + +#if NET7_0_OR_GREATER + return (int)Vector128.Sum(sum); +#else + return Numerics.ReduceSumArm(sum); +#endif + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe int Vp8_Sse16x8_Neon(Span a, Span b) + { + Vector128 sum = Vector128.Zero; + fixed (byte* aRef = &MemoryMarshal.GetReference(a)) + { + fixed (byte* bRef = &MemoryMarshal.GetReference(b)) + { + for (int y = 0; y < 8; y++) + { + sum = AccumulateSSE16Neon(aRef + (y * WebpConstants.Bps), bRef + (y * WebpConstants.Bps), sum); + } + } + } + +#if NET7_0_OR_GREATER + return (int)Vector128.Sum(sum); +#else + return Numerics.ReduceSumArm(sum); +#endif + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static int Vp8_Sse4x4_Neon(Span a, Span b) + { + Vector128 a0 = Load4x4Neon(a).AsByte(); + Vector128 b0 = Load4x4Neon(b).AsByte(); + Vector128 absDiff = AdvSimd.AbsoluteDifference(a0, b0); + Vector64 absDiffLower = absDiff.GetLower().AsByte(); + Vector64 absDiffUpper = absDiff.GetUpper().AsByte(); + Vector128 prod1 = AdvSimd.MultiplyWideningLower(absDiffLower, absDiffLower); + Vector128 prod2 = AdvSimd.MultiplyWideningLower(absDiffUpper, absDiffUpper); + + // pair-wise adds and widen. + Vector128 sum1 = AdvSimd.AddPairwiseWidening(prod1); + Vector128 sum2 = AdvSimd.AddPairwiseWidening(prod2); + + Vector128 sum = AdvSimd.Add(sum1, sum2); +#if NET7_0_OR_GREATER + return (int)Vector128.Sum(sum); +#else + return Numerics.ReduceSumArm(sum); +#endif + } + + // Load all 4x4 pixels into a single Vector128 + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe Vector128 Load4x4Neon(Span src) + { + fixed (byte* srcRef = &MemoryMarshal.GetReference(src)) + { + Vector128 output = Vector128.Zero; + output = AdvSimd.LoadAndInsertScalar(output, 0, (uint*)srcRef); + output = AdvSimd.LoadAndInsertScalar(output, 1, (uint*)(srcRef + WebpConstants.Bps)); + output = AdvSimd.LoadAndInsertScalar(output, 2, (uint*)(srcRef + (WebpConstants.Bps * 2))); + output = AdvSimd.LoadAndInsertScalar(output, 3, (uint*)(srcRef + (WebpConstants.Bps * 3))); + return output; + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static unsafe Vector128 AccumulateSSE16Neon(byte* a, byte* b, Vector128 sum) + { + Vector128 a0 = AdvSimd.LoadVector128(a); + Vector128 b0 = AdvSimd.LoadVector128(b); + + Vector128 absDiff = AdvSimd.AbsoluteDifference(a0, b0); + Vector64 absDiffLower = absDiff.GetLower(); + Vector64 absDiffUpper = absDiff.GetUpper(); + Vector128 prod1 = AdvSimd.MultiplyWideningLower(absDiffLower, absDiffLower); + Vector128 prod2 = AdvSimd.MultiplyWideningLower(absDiffUpper, absDiffUpper); + + // pair-wise adds and widen. + Vector128 sum1 = AdvSimd.AddPairwiseWidening(prod1); + Vector128 sum2 = AdvSimd.AddPairwiseWidening(prod2); + return AdvSimd.Add(sum, AdvSimd.Add(sum1, sum2)); + } + [MethodImpl(InliningOptions.ShortMethod)] private static Vector128 SubtractAndAccumulate(Vector128 a, Vector128 b) { diff --git a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs index fed9c16d4d..fca0d03f23 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs @@ -53,7 +53,7 @@ internal static unsafe class QuantEnc rdCur.Nz = (uint)ReconstructIntra16(it, dqm, rdCur, tmpDst, mode); // Measure RD-score. - rdCur.D = LossyUtils.Vp8_Sse16X16(src, tmpDst); + rdCur.D = LossyUtils.Vp8_Sse16x16(src, tmpDst); rdCur.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto16X16(src, tmpDst, WeightY, scratch)) : 0; rdCur.H = WebpConstants.Vp8FixedCostsI16[mode]; rdCur.R = it.GetCostLuma16(rdCur, proba, res); @@ -145,7 +145,7 @@ internal static unsafe class QuantEnc rdTmp.Nz = (uint)ReconstructIntra4(it, dqm, tmpLevels, src, tmpDst, mode); // Compute RD-score. - rdTmp.D = LossyUtils.Vp8_Sse4X4(src, tmpDst); + rdTmp.D = LossyUtils.Vp8_Sse4x4(src, tmpDst); rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto4X4(src, tmpDst, WeightY, scratch)) : 0; rdTmp.H = modeCosts[mode]; @@ -235,7 +235,7 @@ internal static unsafe class QuantEnc rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode); // Compute RD-score - rdUv.D = LossyUtils.Vp8_Sse16X8(src, tmpDst); + rdUv.D = LossyUtils.Vp8_Sse16x8(src, tmpDst); rdUv.SD = 0; // not calling TDisto here: it tends to flatten areas. rdUv.H = WebpConstants.Vp8FixedCostsUv[mode]; rdUv.R = it.GetCostUv(rdUv, proba, res); @@ -389,7 +389,7 @@ internal static unsafe class QuantEnc for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]); - long score = (LossyUtils.Vp8_Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16); + long score = (LossyUtils.Vp8_Sse16x16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16); if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit) { @@ -436,7 +436,7 @@ internal static unsafe class QuantEnc for (mode = 0; mode < WebpConstants.NumBModes; ++mode) { Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]); - long score = (LossyUtils.Vp8_Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4); + long score = (LossyUtils.Vp8_Sse4x4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4); if (score < bestI4Score) { bestI4Mode = mode; @@ -485,7 +485,7 @@ internal static unsafe class QuantEnc for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]); - long score = (LossyUtils.Vp8_Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv); + long score = (LossyUtils.Vp8_Sse16x8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv); if (score < bestUvScore) { bestMode = mode; diff --git a/src/ImageSharp/IO/ChunkedMemoryStream.cs b/src/ImageSharp/IO/ChunkedMemoryStream.cs index 5f388d24f1..da52f7ca85 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; @@ -48,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; } @@ -71,10 +68,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 +101,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 +123,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 +362,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 +401,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 +427,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 +523,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 +555,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 +572,6 @@ internal sealed class ChunkedMemoryStream : Stream this.Buffer.Dispose(); } - this.Buffer = null; this.isDisposed = true; } } 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..02fa014781 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -28,9 +28,9 @@ 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; + => this.configuration = configuration; /// /// Initializes a new instance of the class. @@ -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); 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..162fae96b3 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; @@ -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; @@ -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,9 @@ public sealed class IptcProfile : IDeepCloneable { Guard.NotNull(other, nameof(other)); - if (other.values != null) + foreach (IptcValue value in other.Values) { - this.values = new Collection(); - - foreach (IptcValue value in other.Values) - { - this.values.Add(value.DeepClone()); - } + this.values.Add(value.DeepClone()); } if (other.Data != null) @@ -78,19 +73,12 @@ 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. /// - public IEnumerable Values - { - get - { - this.Initialize(); - return this.values; - } - } + public IEnumerable Values => this.values; /// public IptcProfile DeepClone() => new(this); @@ -121,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--) { @@ -144,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--) { @@ -312,13 +296,6 @@ public sealed class IptcProfile : IDeepCloneable private void Initialize() { - if (this.values != null) - { - return; - } - - this.values = new Collection(); - if (this.Data == null || this.Data[0] != IptcTagMarkerByte) { return; 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; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index 1da5645835..0256907121 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -140,7 +139,7 @@ public partial struct A8 : IPixel, IPackedVector /// /// The object to compare. /// True if the object is equal to the packed vector. - public override readonly bool Equals(object obj) => obj is A8 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is A8 other && this.Equals(other); /// /// Compares another A8 packed vector with the packed vector. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs index 28b6899b2a..ca68c5aaf9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Abgr32.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -335,7 +334,7 @@ public partial struct Abgr32 : IPixel, IPackedVector } /// - public override readonly bool Equals(object obj) => obj is Abgr32 abgr32 && this.Equals(abgr32); + public override readonly bool Equals(object? obj) => obj is Abgr32 abgr32 && this.Equals(abgr32); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 935751afcd..0c99adb52d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -334,7 +333,7 @@ public partial struct Argb32 : IPixel, IPackedVector } /// - public override readonly bool Equals(object obj) => obj is Argb32 argb32 && this.Equals(argb32); + public override readonly bool Equals(object? obj) => obj is Argb32 argb32 && this.Equals(argb32); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index 751e0bfe86..65b36059b8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -232,7 +231,7 @@ public partial struct Bgr24 : IPixel public readonly bool Equals(Bgr24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); /// - public override readonly bool Equals(object obj) => obj is Bgr24 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Bgr24 other && this.Equals(other); /// public override readonly string ToString() => $"Bgr24({this.B}, {this.G}, {this.R})"; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 21766431cf..ac3b6f8299 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -151,7 +150,7 @@ public partial struct Bgr565 : IPixel, IPackedVector (this.PackedValue & 0x1F) * (1F / 31F)); /// - public override readonly bool Equals(object obj) => obj is Bgr565 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Bgr565 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 204919392d..6b859cda64 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -287,7 +286,7 @@ public partial struct Bgra32 : IPixel, IPackedVector } /// - public override readonly bool Equals(object obj) => obj is Bgra32 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Bgra32 other && this.Equals(other); /// public readonly bool Equals(Bgra32 other) => this.Bgra.Equals(other.Bgra); diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index e342d18455..8ba32c8ac2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -143,7 +142,7 @@ public partial struct Bgra4444 : IPixel, IPackedVector public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); /// - public override readonly bool Equals(object obj) => obj is Bgra4444 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Bgra4444 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index 9d9b7c87fc..e699e5fe58 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -141,7 +140,7 @@ public partial struct Byte4 : IPixel, IPackedVector public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); /// - public override readonly bool Equals(object obj) => obj is Byte4 byte4 && this.Equals(byte4); + public override readonly bool Equals(object? obj) => obj is Byte4 byte4 && this.Equals(byte4); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index a99462c8b9..db1e02adc2 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -140,7 +139,7 @@ public partial struct HalfSingle : IPixel, IPackedVector public readonly float ToSingle() => HalfTypeHelper.Unpack(this.PackedValue); /// - public override readonly bool Equals(object obj) => obj is HalfSingle other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is HalfSingle other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 2f2117c26f..9caae58c95 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -157,7 +156,7 @@ public partial struct HalfVector2 : IPixel, IPackedVector } /// - public override readonly bool Equals(object obj) => obj is HalfVector2 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is HalfVector2 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 859ba7e320..609fec3bd7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -149,7 +148,7 @@ public partial struct HalfVector4 : IPixel, IPackedVector public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); /// - public override readonly bool Equals(object obj) => obj is HalfVector4 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is HalfVector4 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index 5d2539ce13..c6ee8744d9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -153,7 +152,7 @@ public partial struct L16 : IPixel, IPackedVector public void FromRgba64(Rgba64 source) => this.PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); /// - public override readonly bool Equals(object obj) => obj is L16 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is L16 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index 25a79e58fc..383e09b270 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -143,7 +142,7 @@ public partial struct L8 : IPixel, IPackedVector ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); /// - public override readonly bool Equals(object obj) => obj is L8 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is L8 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs index 21e66ed495..7597677a26 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -80,7 +79,7 @@ public partial struct La16 : IPixel, IPackedVector public readonly bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue); /// - public override readonly bool Equals(object obj) => obj is La16 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is La16 other && this.Equals(other); /// public override readonly string ToString() => $"La16({this.L}, {this.A})"; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs index cf53d2217b..cb8fad228d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -82,7 +81,7 @@ public partial struct La32 : IPixel, IPackedVector public readonly bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue); /// - public override readonly bool Equals(object obj) => obj is La32 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is La32 other && this.Equals(other); /// public override readonly string ToString() => $"La32({this.L}, {this.A})"; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index d9d0a53bd8..92b9e6148a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -162,7 +161,7 @@ public partial struct NormalizedByte2 : IPixel, IPackedVector> 8) & 0xFF) / MaxPos); /// - public override readonly bool Equals(object obj) => obj is NormalizedByte2 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is NormalizedByte2 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 5a18d00a2d..f87bb5a602 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -154,7 +153,7 @@ public partial struct NormalizedByte4 : IPixel, IPackedVector this.FromScaledVector4(source.ToScaledVector4()); /// - public override readonly bool Equals(object obj) => obj is NormalizedByte4 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is NormalizedByte4 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 84e5e43625..f77dd69b71 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -163,7 +162,7 @@ public partial struct NormalizedShort2 : IPixel, IPackedVector (short)(this.PackedValue >> 0x10) / MaxPos); /// - public override readonly bool Equals(object obj) => obj is NormalizedShort2 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is NormalizedShort2 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index f8139c547b..989edbd2ba 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -155,7 +154,7 @@ public partial struct NormalizedShort4 : IPixel, IPackedVector public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); /// - public override readonly bool Equals(object obj) => obj is NormalizedShort4 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is NormalizedShort4 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 69f39c8838..0a13a15eda 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -146,7 +145,7 @@ public partial struct Rg32 : IPixel, IPackedVector public readonly Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFFFF, (this.PackedValue >> 16) & 0xFFFF) / Max; /// - public override readonly bool Equals(object obj) => obj is Rg32 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Rg32 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 5f66341571..105618cd96 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -241,7 +240,7 @@ public partial struct Rgb24 : IPixel } /// - public override readonly bool Equals(object obj) => obj is Rgb24 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Rgb24 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index 16673fca2f..1cf63eb24c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -210,7 +209,7 @@ public partial struct Rgb48 : IPixel public void FromRgb48(Rgb48 source) => this = source; /// - public override readonly bool Equals(object obj) => obj is Rgb48 rgb48 && this.Equals(rgb48); + public override readonly bool Equals(object? obj) => obj is Rgb48 rgb48 && this.Equals(rgb48); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 2c59741bf4..7bac1d9208 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -141,7 +140,7 @@ public partial struct Rgba1010102 : IPixel, IPackedVector public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); /// - public override readonly bool Equals(object obj) => obj is Rgba1010102 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Rgba1010102 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index 0d18ac3431..a652c2b339 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers.Binary; using System.Globalization; @@ -267,7 +266,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// The . /// [MethodImpl(InliningOptions.ShortMethod)] - public static bool TryParseHex(string hex, out Rgba32 result) + public static bool TryParseHex(string? hex, out Rgba32 result) { result = default; if (string.IsNullOrWhiteSpace(hex)) @@ -437,7 +436,7 @@ public partial struct Rgba32 : IPixel, IPackedVector } /// - public override readonly bool Equals(object obj) => obj is Rgba32 rgba32 && this.Equals(rgba32); + public override readonly bool Equals(object? obj) => obj is Rgba32 rgba32 && this.Equals(rgba32); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -514,7 +513,7 @@ public partial struct Rgba32 : IPixel, IPackedVector /// /// A rrggbbaa hex value. /// - private static string ToRgbaHex(string hex) + private static string? ToRgbaHex(string hex) { if (hex[0] == '#') { diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 7560892120..81c9591480 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -444,7 +443,7 @@ public partial struct Rgba64 : IPixel, IPackedVector } /// - public override readonly bool Equals(object obj) => obj is Rgba64 rgba64 && this.Equals(rgba64); + public override readonly bool Equals(object? obj) => obj is Rgba64 rgba64 && this.Equals(rgba64); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 3a0426a8e1..899987b712 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Globalization; using System.Numerics; @@ -193,7 +192,7 @@ public partial struct RgbaVector : IPixel } /// - public override readonly bool Equals(object obj) => obj is RgbaVector other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is RgbaVector other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 8eb33afd35..832e8c770f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -164,7 +163,7 @@ public partial struct Short2 : IPixel, IPackedVector public readonly Vector2 ToVector2() => new((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10)); /// - public override readonly bool Equals(object obj) => obj is Short2 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Short2 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index b964c9ec50..c4dc324a13 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -164,7 +163,7 @@ public partial struct Short4 : IPixel, IPackedVector public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); /// - public override readonly bool Equals(object obj) => obj is Short4 other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Short4 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs index fce2f574db..d008d1f67e 100644 --- a/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs +++ b/src/ImageSharp/Processing/AdaptiveThresholdExtensions.cs @@ -13,61 +13,61 @@ public static class AdaptiveThresholdExtensions /// /// Applies Bradley Adaptive Threshold to the image. /// - /// The image this method extends. - /// The . + /// The current image processing context. + /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source) => source.ApplyProcessor(new AdaptiveThresholdProcessor()); /// /// Applies Bradley Adaptive Threshold to the image. /// - /// The image this method extends. + /// The current image processing context. /// Threshold limit (0.0-1.0) to consider for binarization. - /// The . + /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float thresholdLimit) => source.ApplyProcessor(new AdaptiveThresholdProcessor(thresholdLimit)); /// /// Applies Bradley Adaptive Threshold to the image. /// - /// The image this method extends. + /// The current image processing context. /// Upper (white) color for thresholding. /// Lower (black) color for thresholding. - /// The . + /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower) => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower)); /// /// Applies Bradley Adaptive Threshold to the image. /// - /// The image this method extends. + /// The current image processing context. /// Upper (white) color for thresholding. /// Lower (black) color for thresholding. /// Threshold limit (0.0-1.0) to consider for binarization. - /// The . + /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit) => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit)); /// /// Applies Bradley Adaptive Threshold to the image. /// - /// The image this method extends. + /// The current image processing context. /// Upper (white) color for thresholding. /// Lower (black) color for thresholding. /// Rectangle region to apply the processor on. - /// The . + /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, Rectangle rectangle) => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle); /// /// Applies Bradley Adaptive Threshold to the image. /// - /// The image this method extends. + /// The current image processing context. /// Upper (white) color for thresholding. /// Lower (black) color for thresholding. /// Threshold limit (0.0-1.0) to consider for binarization. /// Rectangle region to apply the processor on. - /// The . + /// The . public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit, Rectangle rectangle) => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit), rectangle); } diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index 208455b902..4d95e060dc 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Collections.Concurrent; using SixLabors.ImageSharp.PixelFormats; @@ -17,7 +16,7 @@ internal class DefaultImageProcessorContext : IInternalImageProcessingCo { private readonly bool mutate; private readonly Image source; - private Image destination; + private Image? destination; /// /// Initializes a new instance of the class. @@ -54,7 +53,7 @@ internal class DefaultImageProcessorContext : IInternalImageProcessingCo this.destination = this.source.Clone(); } - return this.destination; + return this.destination!; } /// @@ -87,7 +86,7 @@ internal class DefaultImageProcessorContext : IInternalImageProcessingCo } // Standard processing pipeline. - using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.Configuration, this.destination, rectangle)) + using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.Configuration, this.destination!, rectangle)) { specificProcessor.Execute(); } diff --git a/src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs index 887455b821..e490cdf85f 100644 --- a/src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs @@ -14,9 +14,9 @@ public static class BinaryDitherExtensions /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IDither dither) => BinaryDither(source, dither, Color.White, Color.Black); @@ -24,11 +24,11 @@ public static class BinaryDitherExtensions /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryDither( this IImageProcessingContext source, IDither dither, @@ -39,12 +39,12 @@ public static class BinaryDitherExtensions /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryDither( this IImageProcessingContext source, IDither dither, @@ -54,14 +54,14 @@ public static class BinaryDitherExtensions /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryDither( this IImageProcessingContext source, IDither dither, diff --git a/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs index 802ad06d75..815b059cf5 100644 --- a/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs @@ -15,19 +15,19 @@ public static class BinaryThresholdExtensions /// Applies binarization to the image splitting the pixels at the given threshold with /// Luminance as the color component to be compared to threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdMode.Luminance)); /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// Selects the value to be compared to threshold. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold( this IImageProcessingContext source, float threshold, @@ -38,12 +38,12 @@ public static class BinaryThresholdExtensions /// Applies binarization to the image splitting the pixels at the given threshold with /// Luminance as the color component to be compared to threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold( this IImageProcessingContext source, float threshold, @@ -53,13 +53,13 @@ public static class BinaryThresholdExtensions /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// Selects the value to be compared to threshold. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold( this IImageProcessingContext source, float threshold, @@ -71,11 +71,11 @@ public static class BinaryThresholdExtensions /// Applies binarization to the image splitting the pixels at the given threshold with /// Luminance as the color component to be compared to threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold( this IImageProcessingContext source, float threshold, @@ -86,12 +86,12 @@ public static class BinaryThresholdExtensions /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold /// Selects the value to be compared to threshold. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold( this IImageProcessingContext source, float threshold, @@ -104,14 +104,14 @@ public static class BinaryThresholdExtensions /// Applies binarization to the image splitting the pixels at the given threshold with /// Luminance as the color component to be compared to threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold( this IImageProcessingContext source, float threshold, @@ -123,7 +123,7 @@ public static class BinaryThresholdExtensions /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The image this method extends. + /// The current image processing context. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold @@ -131,7 +131,7 @@ public static class BinaryThresholdExtensions /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BinaryThreshold( this IImageProcessingContext source, float threshold, diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs index 1688e5cc76..e3e6f13ed6 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs @@ -13,44 +13,44 @@ public static class BokehBlurExtensions /// /// Applies a bokeh blur to the image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) => source.ApplyProcessor(new BokehBlurProcessor()); /// /// Applies a bokeh blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'radius' value representing the size of the area to sample. /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. /// The gamma highlight factor to use to emphasize bright spots in the source image - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); /// /// Applies a bokeh blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new BokehBlurProcessor(), rectangle); /// /// Applies a bokeh blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'radius' value representing the size of the area to sample. /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. /// The gamma highlight factor to use to emphasize bright spots in the source image /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs index 85b4888f0d..6611af742b 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -14,36 +14,36 @@ public static class BoxBlurExtensions /// /// Applies a box blur to the image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) => source.ApplyProcessor(new BoxBlurProcessor()); /// /// Applies a box blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'radius' value representing the size of the area to sample. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) => source.ApplyProcessor(new BoxBlurProcessor(radius)); /// /// Applies a box blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'radius' value representing the size of the area to sample. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); /// /// Applies a box blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'radius' value representing the size of the area to sample. /// /// The structure that specifies the portion of the image object to alter. @@ -54,7 +54,7 @@ public static class BoxBlurExtensions /// /// The to use when mapping the pixels outside of the border, in Y direction. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY) { var processor = new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY); diff --git a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs index 7f57ae9837..b044c3966f 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs @@ -14,8 +14,8 @@ public static class DetectEdgesExtensions /// Detects any edges within the image. /// Uses the kernel operating in grayscale mode. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) => DetectEdges(source, KnownEdgeDetectorKernels.Sobel); @@ -23,11 +23,11 @@ public static class DetectEdgesExtensions /// Detects any edges within the image. /// Uses the kernel operating in grayscale mode. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, Rectangle rectangle) => @@ -36,9 +36,9 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image operating in grayscale mode. /// - /// The image this method extends. + /// The current image processing context. /// The 2D edge detector kernel. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetector2DKernel kernel) => @@ -47,12 +47,12 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image using a . /// - /// The image this method extends. + /// The current image processing context. /// The 2D edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetector2DKernel kernel, @@ -66,12 +66,12 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image operating in grayscale mode. /// - /// The image this method extends. + /// The current image processing context. /// The 2D edge detector kernel. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetector2DKernel kernel, @@ -81,7 +81,7 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image using a . /// - /// The image this method extends. + /// The current image processing context. /// The 2D edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. @@ -89,7 +89,7 @@ public static class DetectEdgesExtensions /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetector2DKernel kernel, @@ -104,9 +104,9 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image operating in grayscale mode. /// - /// The image this method extends. + /// The current image processing context. /// The edge detector kernel. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorKernel kernel) => @@ -115,12 +115,12 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image using a . /// - /// The image this method extends. + /// The current image processing context. /// The edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorKernel kernel, @@ -134,12 +134,12 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image operating in grayscale mode. /// - /// The image this method extends. + /// The current image processing context. /// The edge detector kernel. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorKernel kernel, @@ -149,7 +149,7 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image using a . /// - /// The image this method extends. + /// The current image processing context. /// The edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. @@ -157,7 +157,7 @@ public static class DetectEdgesExtensions /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorKernel kernel, @@ -172,9 +172,9 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image operating in grayscale mode. /// - /// The image this method extends. + /// The current image processing context. /// Thecompass edge detector kernel. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorCompassKernel kernel) => @@ -183,12 +183,12 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image using a . /// - /// The image this method extends. + /// The current image processing context. /// Thecompass edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorCompassKernel kernel, @@ -202,12 +202,12 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image operating in grayscale mode. /// - /// The image this method extends. + /// The current image processing context. /// Thecompass edge detector kernel. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorCompassKernel kernel, @@ -217,7 +217,7 @@ public static class DetectEdgesExtensions /// /// Detects any edges within the image using a . /// - /// The image this method extends. + /// The current image processing context. /// Thecompass edge detector kernel. /// /// Whether to convert the image to grayscale before performing edge detection. @@ -225,7 +225,7 @@ public static class DetectEdgesExtensions /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectorCompassKernel kernel, diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs index 9ea4e24af4..b851482008 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -14,36 +14,36 @@ public static class GaussianBlurExtensions /// /// Applies a Gaussian blur to the image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) => source.ApplyProcessor(new GaussianBlurProcessor()); /// /// Applies a Gaussian blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'sigma' value representing the weight of the blur. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); /// /// Applies a Gaussian blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); /// /// Applies a Gaussian blur to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. @@ -54,7 +54,7 @@ public static class GaussianBlurExtensions /// /// The to use when mapping the pixels outside of the border, in Y direction. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY) { var processor = new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY); diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs index 70b8e6f439..4a94df0963 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -14,29 +14,29 @@ public static class GaussianSharpenExtensions /// /// Applies a Gaussian sharpening filter to the image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) => source.ApplyProcessor(new GaussianSharpenProcessor()); /// /// Applies a Gaussian sharpening filter to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'sigma' value representing the weight of the blur. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); /// /// Applies a Gaussian sharpening filter to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext GaussianSharpen( this IImageProcessingContext source, float sigma, @@ -46,7 +46,7 @@ public static class GaussianSharpenExtensions /// /// Applies a Gaussian sharpening filter to the image. /// - /// The image this method extends. + /// The current image processing context. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. @@ -57,7 +57,7 @@ public static class GaussianSharpenExtensions /// /// The to use when mapping the pixels outside of the border, in Y direction. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY) { var processor = new GaussianSharpenProcessor(sigma, borderWrapModeX, borderWrapModeY); diff --git a/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs index 60a80d35e5..a08a398b75 100644 --- a/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/MedianBlurExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Convolution; @@ -14,19 +14,19 @@ public static class MedianBlurExtensions /// /// Applies a median blur on the image. /// - /// The image this method extends. + /// The current image processing context. /// The radius of the area to find the median for. /// /// Whether the filter is applied to alpha as well as the color channels. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha) => source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha)); /// /// Applies a median blur on the image. /// - /// The image this method extends. + /// The current image processing context. /// The radius of the area to find the median for. /// /// Whether the filter is applied to alpha as well as the color channels. @@ -34,7 +34,7 @@ public static class MedianBlurExtensions /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext MedianBlur(this IImageProcessingContext source, int radius, bool preserveAlpha, Rectangle rectangle) => source.ApplyProcessor(new MedianBlurProcessor(radius, preserveAlpha), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs b/src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs index 3eaeb715d3..fbeddb479d 100644 --- a/src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs @@ -14,17 +14,17 @@ public static class DitherExtensions /// /// Dithers the image reducing it to a web-safe palette using . /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Dither(this IImageProcessingContext source) => Dither(source, KnownDitherings.Bayer8x8); /// /// Dithers the image reducing it to a web-safe palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither) => @@ -33,10 +33,10 @@ public static class DitherExtensions /// /// Dithers the image reducing it to a web-safe palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The dithering scale used to adjust the amount of dither. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither, @@ -46,10 +46,10 @@ public static class DitherExtensions /// /// Dithers the image reducing it to the given palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The palette to select substitute colors from. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither, @@ -59,11 +59,11 @@ public static class DitherExtensions /// /// Dithers the image reducing it to the given palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The dithering scale used to adjust the amount of dither. /// The palette to select substitute colors from. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither, @@ -74,23 +74,23 @@ public static class DitherExtensions /// /// Dithers the image reducing it to a web-safe palette using . /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither(this IImageProcessingContext source, Rectangle rectangle) => Dither(source, KnownDitherings.Bayer8x8, rectangle); /// /// Dithers the image reducing it to a web-safe palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither, @@ -100,13 +100,13 @@ public static class DitherExtensions /// /// Dithers the image reducing it to a web-safe palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The dithering scale used to adjust the amount of dither. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither, @@ -117,13 +117,13 @@ public static class DitherExtensions /// /// Dithers the image reducing it to the given palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The palette to select substitute colors from. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither, @@ -134,14 +134,14 @@ public static class DitherExtensions /// /// Dithers the image reducing it to the given palette. /// - /// The image this method extends. + /// The current image processing context. /// The ordered ditherer. /// The dithering scale used to adjust the amount of dither. /// The palette to select substitute colors from. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Dither( this IImageProcessingContext source, IDither dither, diff --git a/src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs index e29b39478f..2c8d76c831 100644 --- a/src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Effects; @@ -15,29 +15,29 @@ public static class OilPaintExtensions /// Alters the colors of the image recreating an oil painting effect with levels and brushSize /// set to 10 and 15 respectively. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext OilPaint(this IImageProcessingContext source) => OilPaint(source, 10, 15); /// /// Alters the colors of the image recreating an oil painting effect with levels and brushSize /// set to 10 and 15 respectively. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) => OilPaint(source, 10, 15, rectangle); /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The image this method extends. + /// The current image processing context. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); @@ -45,13 +45,13 @@ public static class OilPaintExtensions /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The image this method extends. + /// The current image processing context. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext OilPaint( this IImageProcessingContext source, int levels, diff --git a/src/ImageSharp/Processing/Extensions/Effects/PixelRowDelegateExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/PixelRowDelegateExtensions.cs index e7b5d7623e..703b1b2fbb 100644 --- a/src/ImageSharp/Processing/Extensions/Effects/PixelRowDelegateExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Effects/PixelRowDelegateExtensions.cs @@ -14,88 +14,88 @@ public static class PixelRowDelegateExtensions /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation) => ProcessPixelRowsAsVector4(source, rowOperation, PixelConversionModifiers.None); /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. /// The to apply during the pixel conversions. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PixelRowDelegateProcessor(rowOperation, modifiers)); /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle) => ProcessPixelRowsAsVector4(source, rowOperation, rectangle, PixelConversionModifiers.None); /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. /// /// The structure that specifies the portion of the image object to alter. /// /// The to apply during the pixel conversions. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PixelRowDelegateProcessor(rowOperation, modifiers), rectangle); /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation) => ProcessPixelRowsAsVector4(source, rowOperation, PixelConversionModifiers.None); /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. /// The to apply during the pixel conversions. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PositionAwarePixelRowDelegateProcessor(rowOperation, modifiers)); /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle) => ProcessPixelRowsAsVector4(source, rowOperation, rectangle, PixelConversionModifiers.None); /// /// Applies a user defined processing delegate to the image. /// - /// The image this method extends. + /// The current image processing context. /// The user defined processing delegate to use to modify image rows. /// /// The structure that specifies the portion of the image object to alter. /// /// The to apply during the pixel conversions. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PositionAwarePixelRowDelegateProcessor(rowOperation, modifiers), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs index 17654a2061..584887ea17 100644 --- a/src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs @@ -14,28 +14,28 @@ public static class PixelateExtensions /// /// Pixelates an image with the given pixel size. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Pixelate(this IImageProcessingContext source) => Pixelate(source, 4); /// /// Pixelates an image with the given pixel size. /// - /// The image this method extends. + /// The current image processing context. /// The size of the pixels. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) => source.ApplyProcessor(new PixelateProcessor(size)); /// /// Pixelates an image with the given pixel size. /// - /// The image this method extends. + /// The current image processing context. /// The size of the pixels. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Pixelate( this IImageProcessingContext source, int size, diff --git a/src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs index 54f2d2143e..8e44928f9e 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -14,19 +14,19 @@ public static class BlackWhiteExtensions /// /// Applies black and white toning to the image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) => source.ApplyProcessor(new BlackWhiteProcessor()); /// /// Applies black and white toning to the image. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs index e53f22040f..35140c2140 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -18,9 +18,9 @@ public static class BrightnessExtensions /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new BrightnessProcessor(amount)); @@ -31,12 +31,12 @@ public static class BrightnessExtensions /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs index 73c3c29a91..ed1c07a431 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors; @@ -15,21 +15,21 @@ public static class ColorBlindnessExtensions /// /// Applies the given colorblindness simulator to the image. /// - /// The image this method extends. + /// The current image processing context. /// The type of color blindness simulator to apply. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) => source.ApplyProcessor(GetProcessor(colorBlindness)); /// /// Applies the given colorblindness simulator to the image. /// - /// The image this method extends. + /// The current image processing context. /// The type of color blindness simulator to apply. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) => source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); diff --git a/src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs index 5ed5c9a21d..4dd4707bc1 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -18,9 +18,9 @@ public static class ContrastExtensions /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new ContrastProcessor(amount)); @@ -31,12 +31,12 @@ public static class ContrastExtensions /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs index 051afebc97..387307b3df 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -14,21 +14,21 @@ public static class FilterExtensions /// /// Filters an image by the given color matrix /// - /// The image this method extends. + /// The current image processing context. /// The filter color matrix - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) => source.ApplyProcessor(new FilterProcessor(matrix)); /// /// Filters an image by the given color matrix /// - /// The image this method extends. + /// The current image processing context. /// The filter color matrix /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs index 88633719a1..86ef74394e 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs @@ -15,7 +15,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image. /// - /// The image this method extends. + /// The current image processing context. /// The . public static IImageProcessingContext Grayscale(this IImageProcessingContext source) => Grayscale(source, GrayscaleMode.Bt709); @@ -23,7 +23,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image using the given amount. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be between 0 and 1. /// The . public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) @@ -32,7 +32,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image with the given . /// - /// The image this method extends. + /// The current image processing context. /// The formula to apply to perform the operation. /// The . public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) @@ -41,7 +41,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image with the given using the given amount. /// - /// The image this method extends. + /// The current image processing context. /// The formula to apply to perform the operation. /// The proportion of the conversion. Must be between 0 and 1. /// The . @@ -58,7 +58,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// @@ -69,7 +69,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image using the given amount. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. @@ -81,7 +81,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image. /// - /// The image this method extends. + /// The current image processing context. /// The formula to apply to perform the operation. /// /// The structure that specifies the portion of the image object to alter. @@ -93,7 +93,7 @@ public static class GrayscaleExtensions /// /// Applies grayscale toning to the image using the given amount. /// - /// The image this method extends. + /// The current image processing context. /// The formula to apply to perform the operation. /// The proportion of the conversion. Must be between 0 and 1. /// diff --git a/src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs index 3ec339e9c9..a493a6f828 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -14,21 +14,21 @@ public static class HueExtensions /// /// Alters the hue component of the image. /// - /// The image this method extends. + /// The current image processing context. /// The rotation angle in degrees to adjust the hue. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) => source.ApplyProcessor(new HueProcessor(degrees)); /// /// Alters the hue component of the image. /// - /// The image this method extends. + /// The current image processing context. /// The rotation angle in degrees to adjust the hue. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) => source.ApplyProcessor(new HueProcessor(degrees), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs index 3c7845ec83..6b42e7da43 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -14,19 +14,19 @@ public static class InvertExtensions /// /// Inverts the colors of the image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Invert(this IImageProcessingContext source) => source.ApplyProcessor(new InvertProcessor(1F)); /// /// Inverts the colors of the image. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new InvertProcessor(1F), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs index 8aa8260676..5c64e2b785 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -14,19 +14,19 @@ public static class KodachromeExtensions /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) => source.ApplyProcessor(new KodachromeProcessor()); /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new KodachromeProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs index fb937d9728..8542027702 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs @@ -18,9 +18,9 @@ public static class LightnessExtensions /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new LightnessProcessor(amount)); @@ -31,12 +31,12 @@ public static class LightnessExtensions /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new LightnessProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs index 816647bd42..8942fdde01 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs @@ -14,19 +14,19 @@ public static class LomographExtensions /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Lomograph(this IImageProcessingContext source) => source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions())); /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs index e704dd763c..467b98d52b 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -14,21 +14,21 @@ public static class OpacityExtensions /// /// Multiplies the alpha component of the image. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be between 0 and 1. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new OpacityProcessor(amount)); /// /// Multiplies the alpha component of the image. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs index bab05c0365..41678e4313 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs @@ -14,19 +14,19 @@ public static class PolaroidExtensions /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Polaroid(this IImageProcessingContext source) => source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions())); /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs index 03e6ddcba5..5bf9c737ee 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -18,9 +18,9 @@ public static class SaturateExtensions /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new SaturateProcessor(amount)); @@ -31,12 +31,12 @@ public static class SaturateExtensions /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new SaturateProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs index 01ddccba0d..af00b9b329 100644 --- a/src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -14,40 +14,40 @@ public static class SepiaExtensions /// /// Applies sepia toning to the image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Sepia(this IImageProcessingContext source) => Sepia(source, 1F); /// /// Applies sepia toning to the image using the given amount. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be between 0 and 1. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new SepiaProcessor(amount)); /// /// Applies sepia toning to the image. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) => Sepia(source, 1F, rectangle); /// /// Applies sepia toning to the image. /// - /// The image this method extends. + /// The current image processing context. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs b/src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs index ab5b8e3e1b..d7f4ba3594 100644 --- a/src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Normalization; @@ -13,17 +13,17 @@ public static class HistogramEqualizationExtensions /// /// Equalizes the histogram of an image to increases the contrast. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) => HistogramEqualization(source, new HistogramEqualizationOptions()); /// /// Equalizes the histogram of an image to increases the contrast. /// - /// The image this method extends. + /// The current image processing context. /// The histogram equalization options to use. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext HistogramEqualization( this IImageProcessingContext source, HistogramEqualizationOptions options) => diff --git a/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs index 4369f45326..938c880f60 100644 --- a/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs @@ -14,21 +14,21 @@ public static class BackgroundColorExtensions /// /// Replaces the background color of image with the given one. /// - /// The image this method extends. + /// The current image processing context. /// The color to set as the background. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) => BackgroundColor(source, source.GetGraphicsOptions(), color); /// /// Replaces the background color of image with the given one. /// - /// The image this method extends. + /// The current image processing context. /// The color to set as the background. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BackgroundColor( this IImageProcessingContext source, Color color, @@ -38,10 +38,10 @@ public static class BackgroundColorExtensions /// /// Replaces the background color of image with the given one. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting pixel blending. /// The color to set as the background. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BackgroundColor( this IImageProcessingContext source, GraphicsOptions options, @@ -51,13 +51,13 @@ public static class BackgroundColorExtensions /// /// Replaces the background color of image with the given one. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting pixel blending. /// The color to set as the background. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext BackgroundColor( this IImageProcessingContext source, GraphicsOptions options, diff --git a/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs index c588ae9647..9e00f5b304 100644 --- a/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs @@ -14,17 +14,17 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Glow(this IImageProcessingContext source) => Glow(source, source.GetGraphicsOptions()); /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The color to set as the glow. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color) { return Glow(source, source.GetGraphicsOptions(), color); @@ -33,33 +33,33 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The the radius. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) => Glow(source, source.GetGraphicsOptions(), radius); /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) => source.Glow(source.GetGraphicsOptions(), rectangle); /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The color to set as the glow. /// The the radius. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow( this IImageProcessingContext source, Color color, @@ -70,19 +70,19 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting things like blending. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) => source.Glow(options, Color.Black, ValueSize.PercentageOfWidth(0.5f)); /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting things like blending. /// The color to set as the glow. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow( this IImageProcessingContext source, GraphicsOptions options, @@ -92,10 +92,10 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting things like blending. /// The the radius. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow( this IImageProcessingContext source, GraphicsOptions options, @@ -105,12 +105,12 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting things like blending. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow( this IImageProcessingContext source, GraphicsOptions options, @@ -120,14 +120,14 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting things like blending. /// The color to set as the glow. /// The the radius. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Glow( this IImageProcessingContext source, GraphicsOptions options, @@ -139,14 +139,14 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting things like blending. /// The color to set as the glow. /// The the radius. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . private static IImageProcessingContext Glow( this IImageProcessingContext source, GraphicsOptions options, @@ -158,11 +158,11 @@ public static class GlowExtensions /// /// Applies a radial glow effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting things like blending. /// The color to set as the glow. /// The the radius. - /// The to allow chaining of operations. + /// The . private static IImageProcessingContext Glow( this IImageProcessingContext source, GraphicsOptions options, diff --git a/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs index 379a2f32ab..fb9f79d411 100644 --- a/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs @@ -14,27 +14,27 @@ public static class VignetteExtensions /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Vignette(this IImageProcessingContext source) => Vignette(source, source.GetGraphicsOptions()); /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The color to set as the vignette. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) => Vignette(source, source.GetGraphicsOptions(), color); /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The the x-radius. /// The the y-radius. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette( this IImageProcessingContext source, float radiusX, @@ -44,25 +44,25 @@ public static class VignetteExtensions /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) => Vignette(source, source.GetGraphicsOptions(), rectangle); /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The color to set as the vignette. /// The the x-radius. /// The the y-radius. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette( this IImageProcessingContext source, Color color, @@ -74,9 +74,9 @@ public static class VignetteExtensions /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting pixel blending. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) => source.VignetteInternal( options, @@ -87,10 +87,10 @@ public static class VignetteExtensions /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting pixel blending. /// The color to set as the vignette. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette( this IImageProcessingContext source, GraphicsOptions options, @@ -104,11 +104,11 @@ public static class VignetteExtensions /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting pixel blending. /// The the x-radius. /// The the y-radius. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette( this IImageProcessingContext source, GraphicsOptions options, @@ -119,12 +119,12 @@ public static class VignetteExtensions /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting pixel blending. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette( this IImageProcessingContext source, GraphicsOptions options, @@ -139,7 +139,7 @@ public static class VignetteExtensions /// /// Applies a radial vignette effect to an image. /// - /// The image this method extends. + /// The current image processing context. /// The options effecting pixel blending. /// The color to set as the vignette. /// The the x-radius. @@ -147,7 +147,7 @@ public static class VignetteExtensions /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Vignette( this IImageProcessingContext source, GraphicsOptions options, diff --git a/src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs b/src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs index 559477a05e..bf6d2221f4 100644 --- a/src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs @@ -14,40 +14,40 @@ public static class QuantizeExtensions /// /// Applies quantization to the image using the . /// - /// The image this method extends. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext Quantize(this IImageProcessingContext source) => Quantize(source, KnownQuantizers.Octree); /// /// Applies quantization to the image. /// - /// The image this method extends. + /// The current image processing context. /// The quantizer to apply to perform the operation. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) => source.ApplyProcessor(new QuantizeProcessor(quantizer)); /// /// Applies quantization to the image using the . /// - /// The image this method extends. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Quantize(this IImageProcessingContext source, Rectangle rectangle) => Quantize(source, KnownQuantizers.Octree, rectangle); /// /// Applies quantization to the image. /// - /// The image this method extends. + /// The current image processing context. /// The quantizer to apply to perform the operation. /// /// The structure that specifies the portion of the image object to alter. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer, Rectangle rectangle) => source.ApplyProcessor(new QuantizeProcessor(quantizer), rectangle); } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs index a7b8dbf43f..fcd0023e53 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -14,8 +14,8 @@ public static class AutoOrientExtensions /// /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. /// - /// The image to auto rotate. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) => source.ApplyProcessor(new AutoOrientProcessor()); } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs index 434aeb6582..3025806d4f 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -14,21 +14,21 @@ public static class CropExtensions /// /// Crops an image to the given width and height. /// - /// The image to resize. + /// The current image processing context. /// The target image width. /// The target image height. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) => Crop(source, new Rectangle(0, 0, width, height)); /// /// Crops an image to the given rectangle. /// - /// The image to crop. + /// The current image processing context. /// /// The structure that specifies the portion of the image object to retain. /// - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) => source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs index 14e7f9f134..6461a4e8aa 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs @@ -14,17 +14,17 @@ public static class EntropyCropExtensions /// /// Crops an image to the area of greatest entropy using a threshold for entropic density of .5F. /// - /// The image to crop. - /// The to allow chaining of operations. + /// The current image processing context. + /// The . public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) => source.ApplyProcessor(new EntropyCropProcessor()); /// /// Crops an image to the area of greatest entropy. /// - /// The image to crop. + /// The current image processing context. /// The threshold for entropic density. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) => source.ApplyProcessor(new EntropyCropProcessor(threshold)); } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs index 243d818b84..3828572a60 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -14,9 +14,9 @@ public static class FlipExtensions /// /// Flips an image by the given instructions. /// - /// The image to rotate, flip, or both. + /// The current image processing context. /// The to perform the flip. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) => source.ApplyProcessor(new FlipProcessor(flipMode)); } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs index dfade98475..b6db0172dc 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs @@ -12,21 +12,21 @@ public static class PadExtensions /// /// Evenly pads an image to fit the new dimensions. /// - /// The source image to pad. + /// The current image processing context. /// The new width. /// The new height. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) => source.Pad(width, height, default); /// /// Evenly pads an image to fit the new dimensions with the given background color. /// - /// The source image to pad. + /// The current image processing context. /// The new width. /// The new height. /// The background color with which to pad the image. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height, Color color) { Size size = source.GetCurrentSize(); diff --git a/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs index 7580f64690..01f296d096 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs @@ -14,9 +14,9 @@ public static class ResizeExtensions /// /// Resizes an image to the given . /// - /// The image to resize. + /// The current image processing context. /// The target image size. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); @@ -24,10 +24,10 @@ public static class ResizeExtensions /// /// Resizes an image to the given . /// - /// The image to resize. + /// The current image processing context. /// The target image size. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); @@ -35,10 +35,10 @@ public static class ResizeExtensions /// /// Resizes an image to the given width and height. /// - /// The image to resize. + /// The current image processing context. /// The target image width. /// The target image height. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) => Resize(source, width, height, KnownResamplers.Bicubic, false); @@ -46,11 +46,11 @@ public static class ResizeExtensions /// /// Resizes an image to the given width and height. /// - /// The image to resize. + /// The current image processing context. /// The target image width. /// The target image height. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) => Resize(source, width, height, KnownResamplers.Bicubic, compand); @@ -58,11 +58,11 @@ public static class ResizeExtensions /// /// Resizes an image to the given width and height with the given sampler. /// - /// The image to resize. + /// The current image processing context. /// The target image width. /// The target image height. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) => Resize(source, width, height, sampler, false); @@ -70,11 +70,11 @@ public static class ResizeExtensions /// /// Resizes an image to the given width and height with the given sampler. /// - /// The image to resize. + /// The current image processing context. /// The target image size. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) => Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); @@ -82,12 +82,12 @@ public static class ResizeExtensions /// /// Resizes an image to the given width and height with the given sampler. /// - /// The image to resize. + /// The current image processing context. /// The target image width. /// The target image height. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) => Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); @@ -96,7 +96,7 @@ public static class ResizeExtensions /// Resizes an image to the given width and height with the given sampler and /// source rectangle. /// - /// The image to resize. + /// The current image processing context. /// The target image width. /// The target image height. /// The to perform the resampling. @@ -107,7 +107,7 @@ public static class ResizeExtensions /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, @@ -133,7 +133,7 @@ public static class ResizeExtensions /// /// Resizes an image to the given width and height with the given sampler and source rectangle. /// - /// The image to resize. + /// The current image processing context. /// The target image width. /// The target image height. /// The to perform the resampling. @@ -141,7 +141,7 @@ public static class ResizeExtensions /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, @@ -166,9 +166,9 @@ public static class ResizeExtensions /// /// Resizes an image in accordance with the given . /// - /// The image to resize. + /// The current image processing context. /// The resize options. - /// The to allow chaining of operations. + /// The . /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); diff --git a/src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs index ac3c464468..fdf28c595f 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -14,28 +14,28 @@ public static class RotateExtensions /// /// Rotates and flips an image by the given instructions. /// - /// The image to rotate. + /// The current image processing context. /// The to perform the rotation. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) => Rotate(source, (float)rotateMode); /// /// Rotates an image by the given angle in degrees. /// - /// The image to rotate. + /// The current image processing context. /// The angle in degrees to perform the rotation. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) => Rotate(source, degrees, KnownResamplers.Bicubic); /// /// Rotates an image by the given angle in degrees using the specified sampling algorithm. /// - /// The image to rotate. + /// The current image processing context. /// The angle in degrees to perform the rotation. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Rotate( this IImageProcessingContext source, float degrees, diff --git a/src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs index fe66af6aa6..4dc324b5ba 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Processing; @@ -12,10 +12,10 @@ public static class RotateFlipExtensions /// /// Rotates and flips an image by the given instructions. /// - /// The image to rotate, flip, or both. + /// The current image processing context. /// The to perform the rotation. /// The to perform the flip. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) => source.Rotate(rotateMode).Flip(flipMode); } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs index 6b2b8b15f9..09c41b567c 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -14,10 +14,10 @@ public static class SkewExtensions /// /// Skews an image by the given angles in degrees. /// - /// The image to skew. + /// The current image processing context. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) => Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); @@ -25,11 +25,11 @@ public static class SkewExtensions /// /// Skews an image by the given angles in degrees using the specified sampling algorithm. /// - /// The image to skew. + /// The current image processing context. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Skew( this IImageProcessingContext source, float degreesX, diff --git a/src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs index 73ec111c95..4e107a4f0a 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs @@ -13,10 +13,10 @@ public static class SwizzleExtensions /// /// Swizzles an image. /// - /// The image to swizzle. + /// The current image processing context. /// The swizzler function. /// The swizzler function type. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Swizzle(this IImageProcessingContext source, TSwizzler swizzler) where TSwizzler : struct, ISwizzler => source.ApplyProcessor(new SwizzleProcessor(swizzler)); diff --git a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs index fbf3b3a6df..e88f000a7c 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Numerics; @@ -15,9 +15,9 @@ public static class TransformExtensions /// /// Performs an affine transform of an image. /// - /// The image to transform. + /// The current image processing context. /// The affine transform builder. - /// The + /// The . public static IImageProcessingContext Transform( this IImageProcessingContext source, AffineTransformBuilder builder) => @@ -26,62 +26,60 @@ public static class TransformExtensions /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The . + /// The current image processing context. /// The affine transform builder. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + this IImageProcessingContext source, AffineTransformBuilder builder, IResampler sampler) => - ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); + source.Transform(new Rectangle(Point.Empty, source.GetCurrentSize()), builder, sampler); /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The . + /// The current image processing context. /// The source rectangle /// The affine transform builder. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + this IImageProcessingContext source, Rectangle sourceRectangle, AffineTransformBuilder builder, IResampler sampler) { Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); - return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler); + return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The . + /// The current image processing context. /// The source rectangle /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + this IImageProcessingContext source, Rectangle sourceRectangle, Matrix3x2 transform, Size targetDimensions, IResampler sampler) - { - return ctx.ApplyProcessor( + => source.ApplyProcessor( new AffineTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); - } /// /// Performs a projective transform of an image. /// - /// The image to transform. + /// The current image processing context. /// The affine transform builder. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Transform( this IImageProcessingContext source, ProjectiveTransformBuilder builder) => @@ -90,53 +88,51 @@ public static class TransformExtensions /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The . + /// The current image processing context. /// The projective transform builder. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + this IImageProcessingContext source, ProjectiveTransformBuilder builder, IResampler sampler) => - ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); + source.Transform(new Rectangle(Point.Empty, source.GetCurrentSize()), builder, sampler); /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The . + /// The current image processing context. /// The source rectangle /// The projective transform builder. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + this IImageProcessingContext source, Rectangle sourceRectangle, ProjectiveTransformBuilder builder, IResampler sampler) { Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); - return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler); + return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The . + /// The current image processing context. /// The source rectangle /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The to allow chaining of operations. + /// The . public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + this IImageProcessingContext source, Rectangle sourceRectangle, Matrix4x4 transform, Size targetDimensions, IResampler sampler) - { - return ctx.ApplyProcessor( + => source.ApplyProcessor( new ProjectiveTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); - } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs index 4cae2f858e..8128d01196 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.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; @@ -16,8 +15,8 @@ internal sealed class KernelSamplingMap : IDisposable { private readonly MemoryAllocator allocator; private bool isDisposed; - private IMemoryOwner yOffsets; - private IMemoryOwner xOffsets; + private IMemoryOwner? yOffsets; + private IMemoryOwner? xOffsets; /// /// Initializes a new instance of the class. @@ -65,10 +64,10 @@ internal sealed class KernelSamplingMap : IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowOffsetSpan() => this.yOffsets.GetSpan(); + public Span GetRowOffsetSpan() => this.yOffsets!.GetSpan(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetColumnOffsetSpan() => this.xOffsets.GetSpan(); + public Span GetColumnOffsetSpan() => this.xOffsets!.GetSpan(); /// public void Dispose() 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/Drawing/DrawImageProcessor.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs index 9de4f862b8..88b59b7dc6 100644 --- a/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -65,7 +64,7 @@ public class DrawImageProcessor : IImageProcessor { ProcessorFactoryVisitor visitor = new(configuration, this, source, sourceRectangle); this.Image.AcceptVisitor(visitor); - return visitor.Result; + return visitor.Result!; } private class ProcessorFactoryVisitor : IImageVisitor @@ -84,7 +83,7 @@ public class DrawImageProcessor : IImageProcessor this.sourceRectangle = sourceRectangle; } - public IImageProcessor Result { get; private set; } + public IImageProcessor? Result { get; private set; } public void Visit(Image image) where TPixelFg : unmanaged, IPixel diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs index 43d11777db..1136fbc9da 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Diagnostics.CodeAnalysis; @@ -27,9 +26,9 @@ 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 EuclideanPixelMap? pixelMap; private readonly bool isDithering; private bool isDisposed; @@ -41,9 +40,6 @@ public struct OctreeQuantizer : IQuantizer [MethodImpl(InliningOptions.ShortMethod)] public OctreeQuantizer(Configuration configuration, QuantizerOptions options) { - Guard.NotNull(configuration, nameof(configuration)); - Guard.NotNull(options, nameof(options)); - this.Configuration = configuration; this.Options = options; @@ -143,7 +139,7 @@ public struct OctreeQuantizer : IQuantizer // pixel and a black one. if (this.isDithering || color.Equals(default)) { - return (byte)this.pixelMap.GetClosestColor(color, out match); + return (byte)this.pixelMap!.GetClosestColor(color, out match); } ref TPixel paletteRef = ref MemoryMarshal.GetReference(this.palette.Span); @@ -158,8 +154,7 @@ public struct OctreeQuantizer : IQuantizer if (!this.isDisposed) { this.isDisposed = true; - this.paletteOwner?.Dispose(); - this.paletteOwner = null; + this.paletteOwner.Dispose(); this.pixelMap?.Dispose(); this.pixelMap = null; } @@ -183,7 +178,7 @@ public struct OctreeQuantizer : IQuantizer /// /// Store the last node quantized /// - private OctreeNode previousNode; + private OctreeNode? previousNode; /// /// Cache the previous color quantized @@ -221,7 +216,7 @@ public struct OctreeQuantizer : IQuantizer /// /// Gets the array of reducible nodes /// - private OctreeNode[] ReducibleNodes + private OctreeNode?[] ReducibleNodes { [MethodImpl(InliningOptions.ShortMethod)] get; @@ -311,7 +306,7 @@ public struct OctreeQuantizer : IQuantizer } // Reduce the node most recently added to the list at level 'index' - OctreeNode node = this.ReducibleNodes[index]; + OctreeNode node = this.ReducibleNodes[index]!; this.ReducibleNodes[index] = node.NextReducible; // Decrement the leaf count after reducing the node @@ -330,7 +325,7 @@ public struct OctreeQuantizer : IQuantizer /// /// Pointers to any child nodes /// - private readonly OctreeNode[] children; + private readonly OctreeNode?[]? children; /// /// Flag indicating that this is a leaf node @@ -395,7 +390,7 @@ public struct OctreeQuantizer : IQuantizer /// /// Gets the next reducible node /// - public OctreeNode NextReducible + public OctreeNode? NextReducible { [MethodImpl(InliningOptions.ShortMethod)] get; @@ -423,7 +418,7 @@ public struct OctreeQuantizer : IQuantizer // Go to the next level down in the tree int index = GetColorIndex(ref color, level); - OctreeNode child = this.children[index]; + OctreeNode? child = this.children![index]; if (child is null) { // Create a new child node and store it in the array @@ -448,7 +443,7 @@ public struct OctreeQuantizer : IQuantizer // Loop through all children and add their information to this node for (int index = 0; index < 8; index++) { - OctreeNode child = this.children[index]; + OctreeNode? child = this.children![index]; if (child != null) { this.red += child.red; @@ -495,7 +490,7 @@ public struct OctreeQuantizer : IQuantizer // Loop through children looking for leaves for (int i = 0; i < 8; i++) { - this.children[i]?.ConstructPalette(palette, ref index); + this.children![i]?.ConstructPalette(palette, ref index); } } } @@ -517,7 +512,7 @@ public struct OctreeQuantizer : IQuantizer } int colorIndex = GetColorIndex(ref pixel, level); - OctreeNode child = this.children[colorIndex]; + OctreeNode? child = this.children![colorIndex]; int index = 0; if (child != null) diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index f3ed39c957..fe4af9005a 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -51,7 +51,7 @@ public class PaletteQuantizer : IQuantizer // 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 7b351a61f5..86db9f6f01 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -18,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. @@ -66,9 +65,5 @@ internal struct PaletteQuantizer : IQuantizer => (byte)this.pixelMap.GetClosestColor(color, out match); /// - public void Dispose() - { - this.pixelMap?.Dispose(); - this.pixelMap = null; - } + public void Dispose() => this.pixelMap.Dispose(); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs index f52cfd6ea7..b3d03d9338 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs @@ -17,7 +17,7 @@ public class QuantizerOptions /// Gets or sets the algorithm to apply to the output image. /// Defaults to ; set to for no dithering. /// - public IDither Dither { get; set; } = QuantizerConstants.DefaultDither; + public IDither? Dither { get; set; } = QuantizerConstants.DefaultDither; /// /// Gets or sets the dithering scale used to adjust the amount of dither. Range 0..1. diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs index 167cf91282..53203f94a0 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs @@ -146,7 +146,7 @@ public static class QuantizerUtilities where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { - IDither dither = quantizer.Options.Dither; + IDither? dither = quantizer.Options.Dither; Buffer2D sourceBuffer = source.PixelBuffer; if (dither is null) diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs index e0aede058a..edf293d39e 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Buffers; using System.Diagnostics.CodeAnalysis; @@ -70,13 +69,13 @@ 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; - private EuclideanPixelMap pixelMap; + private EuclideanPixelMap? pixelMap; private readonly bool isDithering; private bool isDisposed; @@ -175,7 +174,7 @@ internal struct WuQuantizer : IQuantizer { if (this.isDithering) { - return (byte)this.pixelMap.GetClosestColor(color, out match); + return (byte)this.pixelMap!.GetClosestColor(color, out match); } Rgba32 rgba = default; @@ -203,9 +202,6 @@ internal struct WuQuantizer : IQuantizer this.momentsOwner?.Dispose(); this.tagsOwner?.Dispose(); this.paletteOwner?.Dispose(); - this.momentsOwner = null; - this.tagsOwner = null; - this.paletteOwner = null; this.pixelMap?.Dispose(); this.pixelMap = null; } @@ -869,7 +865,7 @@ internal struct WuQuantizer : IQuantizer public int Volume; /// - public override readonly bool Equals(object obj) + public override readonly bool Equals(object? obj) => obj is Box box && this.Equals(box); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs index b9ff551691..c5c2a778eb 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -20,8 +19,8 @@ internal class AffineTransformProcessor : TransformProcessor, IR private readonly Size destinationSize; private readonly Matrix3x2 transformMatrix; private readonly IResampler resampler; - private ImageFrame source; - private ImageFrame destination; + private ImageFrame? source; + private ImageFrame? destination; /// /// Initializes a new instance of the class. @@ -53,8 +52,8 @@ internal class AffineTransformProcessor : TransformProcessor, IR where TResampler : struct, IResampler { Configuration configuration = this.Configuration; - ImageFrame source = this.source; - ImageFrame destination = this.destination; + ImageFrame source = this.source!; + ImageFrame destination = this.destination!; Matrix3x2 matrix = this.transformMatrix; // Handle transforms that result in output identical to the original. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs index 7d1a10926c..b741dc4ee6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Numerics; using System.Runtime.CompilerServices; @@ -20,8 +19,8 @@ internal class ProjectiveTransformProcessor : TransformProcessor private readonly Size destinationSize; private readonly IResampler resampler; private readonly Matrix4x4 transformMatrix; - private ImageFrame source; - private ImageFrame destination; + private ImageFrame? source; + private ImageFrame? destination; /// /// Initializes a new instance of the class. @@ -53,8 +52,8 @@ internal class ProjectiveTransformProcessor : TransformProcessor where TResampler : struct, IResampler { Configuration configuration = this.Configuration; - ImageFrame source = this.source; - ImageFrame destination = this.destination; + ImageFrame source = this.source!; + ImageFrame destination = this.destination!; Matrix4x4 matrix = this.transformMatrix; // Handle transforms that result in output identical to the original. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index ba96e76ae0..98c2523fae 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -1,6 +1,5 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -#nullable disable using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; @@ -21,7 +20,7 @@ internal class ResizeProcessor : TransformProcessor, IResampling private readonly int destinationHeight; private readonly IResampler resampler; private readonly Rectangle destinationRectangle; - private Image destination; + private Image? destination; public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) @@ -56,7 +55,7 @@ internal class ResizeProcessor : TransformProcessor, IResampling { Configuration configuration = this.Configuration; Image source = this.Source; - Image destination = this.destination; + Image destination = this.destination!; Rectangle sourceRectangle = this.SourceRectangle; Rectangle destinationRectangle = this.destinationRectangle; bool compand = this.options.Compand; 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/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 022451668b..b4facfa3fe 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; @@ -49,8 +50,11 @@ 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(0.0001F), + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0002F : 0.0001F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 425d12497c..80789178d1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -302,8 +302,11 @@ public partial class JpegDecoderTests } // https://github.com/SixLabors/ImageSharp/issues/2315 + // https://github.com/SixLabors/ImageSharp/issues/2334 [Theory] [WithFile(TestImages.Jpeg.Issues.Issue2315_NotEnoughBytes, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Issues.Issue2334_NotEnoughBytesA, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Issues.Issue2334_NotEnoughBytesB, PixelTypes.Rgba32)] public void Issue2315_DecodeWorks(TestImageProvider provider) where TPixel : unmanaged, IPixel { 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] 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/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); diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs index 3c9a2f8261..0bbe1984f0 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; @@ -758,8 +759,11 @@ 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(0.0001F), + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0016F : 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 8c43fd81d8..75e7d39091 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; @@ -27,6 +28,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester [InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)] [InlineData(SmallRgbDeflate, 24, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Calliphora_GrayscaleUncompressed, 8, 200, 298, 96, 96, PixelResolutionUnit.PixelsPerInch)] + [InlineData(Calliphora_GrayscaleUncompressed16Bit, 16, 200, 298, 96, 96, PixelResolutionUnit.PixelsPerInch)] [InlineData(Flower4BitPalette, 4, 73, 43, 72, 72, PixelResolutionUnit.PixelsPerInch)] public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit) { @@ -34,7 +36,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester using MemoryStream stream = new(testFile.Bytes, false); ImageInfo info = Image.Identify(stream); - Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel); + Assert.Equal(expectedPixelSize, info.PixelType.BitsPerPixel); Assert.Equal(expectedWidth, info.Width); Assert.Equal(expectedHeight, info.Height); Assert.NotNull(info.Metadata); @@ -64,6 +66,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester [Theory] [WithFile(RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleUncompressed16Bit, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_Uncompressed(TestImageProvider provider) @@ -599,6 +602,8 @@ public class TiffDecoderTests : TiffDecoderBaseTester [WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleDeflate, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleDeflate_Predictor, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleDeflate16Bit, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleDeflate_Predictor16Bit, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbDeflate_Predictor, PixelTypes.Rgba32)] [WithFile(RgbDeflate, PixelTypes.Rgba32)] [WithFile(RgbDeflatePredictor, PixelTypes.Rgba32)] @@ -615,6 +620,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester [WithFile(Calliphora_RgbPaletteLzw_Predictor, PixelTypes.Rgba32)] [WithFile(Calliphora_RgbLzwPredictor, PixelTypes.Rgba32)] [WithFile(Calliphora_GrayscaleLzw_Predictor, PixelTypes.Rgba32)] + [WithFile(Calliphora_GrayscaleLzw_Predictor16Bit, PixelTypes.Rgba32)] [WithFile(SmallRgbLzw, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_LzwCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); @@ -760,8 +766,11 @@ 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( - ImageComparer.Exact, + TestEnvironment.OSArchitecture == Architecture.Arm64 ? ImageComparer.TolerantPercentage(0.0006F) : ImageComparer.Exact, provider, testOutputDetails: details, appendPixelTypeToFileName: false); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 97445ad6cf..f8aa1551fc 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -17,6 +17,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffBitsPerPixel.Bit8)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffBitsPerPixel.Bit8)] [InlineData(TiffPhotometricInterpretation.WhiteIsZero, TiffBitsPerPixel.Bit8)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffBitsPerPixel.Bit16)] //// Unsupported TiffPhotometricInterpretation should default to 24 bits [InlineData(TiffPhotometricInterpretation.CieLab, TiffBitsPerPixel.Bit24)] [InlineData(TiffPhotometricInterpretation.ColorFilterArray, TiffBitsPerPixel.Bit24)] @@ -28,7 +29,9 @@ public class TiffEncoderTests : TiffEncoderBaseTester { // arrange var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation }; - using Image input = new Image(10, 10); + using Image input = expectedBitsPerPixel is TiffBitsPerPixel.Bit16 + ? new Image(10, 10) + : new Image(10, 10); using var memStream = new MemoryStream(); // act @@ -44,6 +47,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester [Theory] [InlineData(TiffBitsPerPixel.Bit24)] + [InlineData(TiffBitsPerPixel.Bit16)] [InlineData(TiffBitsPerPixel.Bit8)] [InlineData(TiffBitsPerPixel.Bit4)] [InlineData(TiffBitsPerPixel.Bit1)] @@ -117,14 +121,17 @@ public class TiffEncoderTests : TiffEncoderBaseTester [Theory] [InlineData(null, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate, TiffBitsPerPixel.Bit16, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Deflate, TiffBitsPerPixel.Bit8, TiffCompression.Deflate)] [InlineData(null, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.PackBits, TiffBitsPerPixel.Bit24, TiffCompression.PackBits)] - [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits, TiffBitsPerPixel.Bit16, TiffCompression.PackBits)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] + [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.PackBits, TiffBitsPerPixel.Bit8, TiffCompression.PackBits)] [InlineData(null, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Lzw, TiffBitsPerPixel.Bit24, TiffCompression.Lzw)] + [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw, TiffBitsPerPixel.Bit16, TiffCompression.Lzw)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] [InlineData(TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, TiffBitsPerPixel.Bit8, TiffCompression.Lzw)] [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.CcittGroup3Fax, TiffBitsPerPixel.Bit1, TiffCompression.CcittGroup3Fax)] @@ -143,7 +150,9 @@ public class TiffEncoderTests : TiffEncoderBaseTester { // arrange var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation, Compression = compression }; - using Image input = new Image(10, 10); + using Image input = expectedBitsPerPixel is TiffBitsPerPixel.Bit16 + ? new Image(10, 10) + : new Image(10, 10); using var memStream = new MemoryStream(); // act @@ -160,6 +169,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit1)] [WithFile(GrayscaleUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit8)] + [WithFile(GrayscaleUncompressed16Bit, PixelTypes.L16, TiffBitsPerPixel.Bit16)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffBitsPerPixel.Bit24)] [WithFile(Rgb4BitPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Bit4)] [WithFile(RgbPalette, PixelTypes.Rgba32, TiffBitsPerPixel.Bit8)] @@ -406,6 +416,36 @@ public class TiffEncoderTests : TiffEncoderBaseTester where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.PaletteColor, TiffCompression.Lzw, TiffPredictor.Horizontal, useExactComparer: false, compareTolerance: 0.001f); + [Theory] + [WithFile(Calliphora_GrayscaleUncompressed16Bit, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray16_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero); + + [Theory] + [WithFile(Calliphora_GrayscaleUncompressed16Bit, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray16_WithDeflateCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate); + + [Theory] + [WithFile(Calliphora_GrayscaleUncompressed16Bit, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray16_WithDeflateCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Deflate, TiffPredictor.Horizontal); + + [Theory] + [WithFile(Calliphora_GrayscaleUncompressed16Bit, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray16_WithLzwCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw); + + [Theory] + [WithFile(Calliphora_GrayscaleUncompressed16Bit, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray16_WithLzwCompressionAndPredictor_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Lzw, TiffPredictor.Horizontal); + + [Theory] + [WithFile(Calliphora_GrayscaleUncompressed16Bit, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeGray16_WithPackBitsCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit16, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits); + [Theory] [WithFile(Calliphora_BiColorUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeBiColor_BlackIsZero_Works(TestImageProvider provider) @@ -473,6 +513,7 @@ public class TiffEncoderTests : TiffEncoderBaseTester [Theory] [WithFile(GrayscaleUncompressed, PixelTypes.L8, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits)] + [WithFile(GrayscaleUncompressed16Bit, PixelTypes.L16, TiffPhotometricInterpretation.BlackIsZero, TiffCompression.PackBits)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate)] [WithFile(RgbUncompressed, PixelTypes.Rgb24, TiffPhotometricInterpretation.Rgb, TiffCompression.None)] [WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffPhotometricInterpretation.Rgb, TiffCompression.None)] diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs index 51c0b6ef7e..5b09a244b5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs @@ -75,6 +75,7 @@ public class TiffMetadataTests [Theory] [InlineData(Calliphora_BiColorUncompressed, 1)] [InlineData(GrayscaleUncompressed, 8)] + [InlineData(GrayscaleUncompressed16Bit, 16)] [InlineData(RgbUncompressed, 24)] public void Identify_DetectsCorrectBitPerPixel(string imagePath, int expectedBitsPerPixel) { 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/Formats/WebP/LossyUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs index 69b503b5e5..73e7044f5b 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LossyUtilsTests.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.Lossy; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -77,151 +78,73 @@ public class LossyUtilsTests private static void RunVp8Sse16X16Test() { // arrange - byte[] a = + Random rand = new(1234); + byte[] a = new byte[512 * 10]; + byte[] b = new byte[512 * 10]; + for (int i = 0; i < a.Length; i++) { - 154, 154, 151, 151, 149, 148, 151, 157, 163, 163, 154, 132, 102, 98, 104, 108, 107, 104, 104, 103, - 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, 147, 147, 146, 159, - 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, 172, 172, 172, 168, - 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, 93, 90, 102, 107, - 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, 150, 149, 152, 151, - 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, 102, 102, 121, 117, - 170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, 154, 154, 151, 132, - 92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, 171, 178, 172, 176, - 152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, 102, 100, 107, 100, - 101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, 160, 162, 161, 153, - 150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, 171, 179, 178, 172, - 171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, 86, 86, 102, 105, - 102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, 154, 152, 158, 163, - 150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 154, 151, 165, 156, 141, 137, 146, 158, 152, 159, 152, 133, - 90, 88, 99, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 154, 160, 164, 150, 126, 127, 149, 159, 155, 161, 153, 131, 84, 86, 97, 103, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 157, 167, 157, 137, 102, 128, 155, 161, - 157, 159, 154, 134, 84, 82, 97, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 163, 163, 150, 113, 78, 132, 156, 162, 159, 160, 154, 132, 83, 78, 91, 97, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 163, 157, 137, 80, 78, - 131, 154, 163, 157, 159, 149, 131, 82, 77, 94, 100, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 159, 151, 108, 72, 88, 132, 156, 162, 159, 157, 151, 130, 79, 78, - 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 151, 130, - 82, 82, 89, 134, 154, 161, 161, 157, 152, 129, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204 - }; - - byte[] b = + a[i] = (byte)rand.Next(byte.MaxValue); + b[i] = (byte)rand.Next(byte.MaxValue); + } + int[] expected = { 2533110, 2818581, 2984663, 2891188, 2855134, 2634604, 2466504, 3061747, 2626010, 2640965 }; + + // act + assert + int offset = 0; + for (int i = 0; i < expected.Length; i++) { - 150, 150, 150, 150, 146, 149, 152, 154, 164, 166, 154, 132, 99, 92, 106, 112, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, - 161, 164, 151, 130, 93, 86, 100, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127, 93, 86, 100, 106, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 150, 150, 150, 150, - 146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 148, 148, 148, 148, 149, 158, 162, 159, 155, 155, 153, 129, - 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 154, 154, 154, 156, 161, 159, 152, - 155, 155, 153, 129, 94, 87, 101, 106, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129, 94, 87, 101, 106, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 153, 157, 162, - 150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 152, 156, 158, 157, 140, 137, 145, 159, 155, 160, 150, 131, - 89, 88, 102, 101, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 153, 161, 160, 149, 118, 128, 147, 162, 155, 160, 150, 131, 86, 85, 99, 98, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 154, 165, 161, 144, 96, 128, 154, 159, 155, - 160, 150, 131, 83, 82, 97, 96, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 161, 160, 149, 105, 78, 127, 156, 170, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 160, 160, 133, 85, 81, 129, 155, - 167, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 156, 147, 109, 76, 85, 130, 153, 163, 156, 156, 154, 130, 81, 77, 95, 102, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 152, 128, 87, 83, - 88, 132, 152, 159, 156, 156, 154, 130, 81, 77, 95, 102, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204 - }; - - int expected = 2063; + int actual = LossyUtils.Vp8_Sse16x16(a.AsSpan(offset), b.AsSpan(offset)); + Assert.Equal(expected[i], actual); - // act - int actual = LossyUtils.Vp8_Sse16X16(a, b); - - // assert - Assert.Equal(expected, actual); + offset += 512; + } } private static void RunVp8Sse16X8Test() { // arrange - byte[] a = + Random rand = new(1234); + byte[] a = new byte[256 * 10]; + byte[] b = new byte[256 * 10]; + for (int i = 0; i < a.Length; i++) { - 107, 104, 104, 103, 101, 106, 123, 119, 170, 171, 172, 171, 168, 175, 171, 173, 151, 151, 149, 150, - 147, 147, 146, 159, 164, 165, 154, 129, 92, 90, 101, 105, 104, 103, 104, 101, 100, 105, 123, 117, - 172, 172, 172, 168, 170, 177, 170, 175, 151, 149, 150, 150, 147, 147, 156, 161, 161, 161, 151, 126, - 93, 90, 102, 107, 104, 103, 104, 101, 104, 104, 122, 117, 172, 172, 170, 168, 170, 177, 172, 175, - 150, 149, 152, 151, 148, 151, 160, 159, 157, 157, 148, 133, 96, 90, 103, 107, 104, 104, 101, 100, - 102, 102, 121, 117, 170, 170, 169, 171, 171, 179, 173, 175, 149, 151, 152, 151, 148, 154, 162, 157, - 154, 154, 151, 132, 92, 89, 101, 108, 104, 102, 101, 101, 103, 103, 123, 118, 171, 168, 177, 173, - 171, 178, 172, 176, 152, 152, 152, 151, 154, 162, 161, 155, 149, 157, 156, 129, 92, 87, 101, 107, - 102, 100, 107, 100, 101, 102, 123, 118, 170, 175, 182, 172, 171, 179, 173, 175, 152, 151, 154, 155, - 160, 162, 161, 153, 150, 156, 153, 129, 92, 91, 102, 106, 100, 109, 115, 99, 101, 102, 124, 120, - 171, 179, 178, 172, 171, 181, 171, 173, 154, 154, 154, 162, 160, 158, 156, 152, 153, 157, 151, 128, - 86, 86, 102, 105, 102, 122, 114, 99, 101, 102, 125, 120, 178, 173, 177, 172, 171, 180, 172, 173, - 154, 152, 158, 163, 150, 148, 148, 156, 151, 158, 152, 129, 87, 87, 101, 105 - }; - - byte[] b = + a[i] = (byte)rand.Next(byte.MaxValue); + b[i] = (byte)rand.Next(byte.MaxValue); + } + int[] expected = { 1298274, 1234836, 1325264, 1493317, 1551995, 1432668, 1407891, 1483297, 1537930, 1317204 }; + + // act + assert + int offset = 0; + for (int i = 0; i < expected.Length; i++) { - 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, - 146, 149, 152, 154, 161, 164, 151, 130, 93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114, - 171, 171, 171, 171, 171, 177, 169, 175, 150, 150, 150, 150, 146, 149, 152, 154, 158, 161, 148, 127, - 93, 86, 100, 106, 103, 103, 103, 103, 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, - 150, 150, 150, 150, 146, 149, 152, 154, 156, 159, 146, 125, 99, 92, 106, 112, 103, 103, 103, 103, - 101, 106, 122, 114, 171, 171, 171, 171, 171, 177, 169, 175, 148, 148, 148, 148, 149, 158, 162, 159, - 155, 155, 153, 129, 94, 87, 101, 106, 102, 100, 100, 102, 100, 101, 120, 122, 170, 176, 176, 170, - 174, 180, 171, 177, 151, 151, 151, 151, 152, 159, 161, 156, 155, 155, 153, 129, 94, 87, 101, 106, - 102, 105, 105, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, 154, 154, 154, 154, - 156, 161, 159, 152, 155, 155, 153, 129, 94, 87, 101, 106, 102, 112, 112, 102, 100, 101, 120, 122, - 170, 176, 176, 170, 174, 180, 171, 177, 156, 156, 156, 156, 159, 162, 158, 149, 155, 155, 153, 129, - 94, 87, 101, 106, 102, 117, 117, 102, 100, 101, 120, 122, 170, 176, 176, 170, 174, 180, 171, 177, - 152, 153, 157, 162, 150, 149, 149, 151, 155, 160, 150, 131, 91, 90, 104, 104 - }; - - int expected = 749; - - // act - int actual = LossyUtils.Vp8_Sse16X8(a, b); + int actual = LossyUtils.Vp8_Sse16x8(a.AsSpan(offset), b.AsSpan(offset)); + Assert.Equal(expected[i], actual); - // assert - Assert.Equal(expected, actual); + offset += 256; + } } private static void RunVp8Sse4X4Test() { // arrange - byte[] a = + Random rand = new(1234); + byte[] a = new byte[128 * 10]; + byte[] b = new byte[128 * 10]; + for (int i = 0; i < a.Length; i++) { - 27, 27, 28, 29, 29, 28, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, - 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 29, 29, 28, - 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 27, 27, 26, - 26, 26, 26, 27, 27, 27, 28, 28, 29, 29, 28, 28, 27, 129, 129, 129, 129, 129, 129, 129, 129, 128, - 128, 128, 128, 128, 128, 128, 128, 28, 27, 27, 26, 26, 27, 27, 28, 27, 28, 28, 29, 29, 28, 28, 27, - 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128 - }; - - byte[] b = + a[i] = (byte)rand.Next(byte.MaxValue); + b[i] = (byte)rand.Next(byte.MaxValue); + } + int[] expected = { 194133, 125861, 165966, 195688, 106491, 173015, 266960, 200272, 311224, 122545 }; + + // act + assert + int offset = 0; + for (int i = 0; i < expected.Length; i++) { - 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, - 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 204, 204, 204, 204, 204, 204, 204, 204, 204, - 204, 204, 204, 204, 204, 204, 204, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204 - }; + int actual = LossyUtils.Vp8_Sse4x4(a.AsSpan(offset), b.AsSpan(offset)); + Assert.Equal(expected[i], actual); - int expected = 27; - - // act - int actual = LossyUtils.Vp8_Sse4X4(a, b); - - // assert - Assert.Equal(expected, actual); + offset += 128; + } } private static void RunMean16x4Test() @@ -300,53 +223,99 @@ public class LossyUtilsTests public void HadamardTransform_Works() => RunHadamardTransformTest(); [Fact] - public void TransformTwo_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.AllowAll); + public void TransformTwo_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.AllowAll); [Fact] - public void TransformTwo_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.DisableHWIntrinsic); + public void TransformTwo_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformTwoTest, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void TransformOne_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.AllowAll); + public void TransformOne_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.AllowAll); [Fact] - public void TransformOne_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.DisableHWIntrinsic); + public void TransformOne_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformOneTest, HwIntrinsics.DisableHWIntrinsic); + // This will test the AVX2 or ARM version. [Fact] - public void Vp8Sse16X16_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.AllowAll); + public void Vp8Sse16X16_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.AllowAll); + // This will test the SSE2 version. [Fact] - public void Vp8Sse16X16_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableSSE2); + public void Vp8Sse16X16_WithoutAVX2_Works() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return; + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableAVX2); + } + // This will test the fallback scalar version. [Fact] - public void Vp8Sse16X16_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableAVX2); + public void Vp8Sse16X16_WithoutHwIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X16Test, HwIntrinsics.DisableHWIntrinsic); + // This will test the AVX2 or ARM version. [Fact] - public void Vp8Sse16X8_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.AllowAll); + public void Vp8Sse16X8_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.AllowAll); + // This will test the SSE2 version. [Fact] - public void Vp8Sse16X8_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableSSE2); + public void Vp8Sse16X8_WithoutAVX2_Works() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return; + } + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableAVX2); + } + + // This will test the fallback scalar version. [Fact] - public void Vp8Sse16X8_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableAVX2); + public void Vp8Sse16X8_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse16X8Test, HwIntrinsics.DisableHWIntrinsic); + // This will test the AVX2 version or ARM version. [Fact] - public void Vp8Sse4X4_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.AllowAll); + public void Vp8Sse4X4_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.AllowAll); + // This will test the SSE2 version. [Fact] - public void Vp8Sse4X4_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableSSE2); + public void Vp8Sse4X4_WithoutAVX2_Works() + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + return; + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableAVX2); + } + // This will test the fallback scalar version. [Fact] - public void Vp8Sse4X4_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableAVX2); + public void Vp8Sse4X4_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunVp8Sse4X4Test, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void Mean16x4_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.AllowAll); + public void Mean16x4_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.AllowAll); [Fact] - public void Mean16x4_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.DisableHWIntrinsic); + public void Mean16x4_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunMean16x4Test, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void HadamardTransform_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.AllowAll); + public void HadamardTransform_WithHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.AllowAll); [Fact] - public void HadamardTransform_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.DisableHWIntrinsic); + public void HadamardTransform_WithoutHardwareIntrinsics_Works() => + FeatureTestRunner.RunWithHwIntrinsicsFeature(RunHadamardTransformTest, HwIntrinsics.DisableHWIntrinsic); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index 003e5ad4c9..010af3fbbe 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; @@ -365,8 +366,11 @@ 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(0.0007F), + ImageComparer.TolerantPercentage(TestEnvironment.OSArchitecture == Architecture.Arm64 ? 0.0156F : 0.0007F), provider, testOutputDetails: details, appendPixelTypeToFileName: false); 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)); } 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/Memory/Allocators/MemoryDiagnosticsTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs index 1876d26479..b8dd4be8df 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/MemoryDiagnosticsTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; @@ -94,7 +94,7 @@ public class MemoryDiagnosticsTests if (subscribeLeakHandle) { // Make sure at least some of the leak callbacks have time to complete on the ThreadPool - Thread.Sleep(200); + Thread.Sleep(1000); Assert.True(leakCounter > 3, $"leakCounter did not count enough leaks ({leakCounter} only)"); } diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs index 2f47e6ff3a..17f04795dc 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Memory.Internals; @@ -66,6 +67,12 @@ public partial class UniformUnmanagedMemoryPoolTests return; } + if (TestEnvironment.OSArchitecture == Architecture.Arm64) + { + // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + return; + } + RemoteExecutor.Invoke(RunTest).Dispose(); static void RunTest() diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index bd7c4f2bdb..3403e3f17e 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -261,6 +261,12 @@ public class UniformUnmanagedPoolMemoryAllocatorTests return; } + if (TestEnvironment.OSArchitecture == Architecture.Arm64) + { + // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + return; + } + if (!TestEnvironment.RunsOnCI) { // This may fail in local runs resulting in high memory load. @@ -323,6 +329,12 @@ public class UniformUnmanagedPoolMemoryAllocatorTests return; } + if (TestEnvironment.OSArchitecture == Architecture.Arm64) + { + // Skip on ARM64: https://github.com/SixLabors/ImageSharp/issues/2342 + return; + } + if (!TestEnvironment.RunsOnCI) { // This may fail in local runs resulting in high memory load. 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] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 22a48ebee4..7c383d389d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -282,6 +282,8 @@ public static class TestImages public const string Issue2133_DeduceColorSpace = "Jpg/issues/Issue2133.jpg"; public const string Issue2136_ScanMarkerExtraneousBytes = "Jpg/issues/Issue2136-scan-segment-extraneous-bytes.jpg"; public const string Issue2315_NotEnoughBytes = "Jpg/issues/issue-2315.jpg"; + public const string Issue2334_NotEnoughBytesA = "Jpg/issues/issue-2334-a.jpg"; + public const string Issue2334_NotEnoughBytesB = "Jpg/issues/issue-2334-b.jpg"; public static class Fuzz { @@ -375,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"; @@ -765,6 +767,10 @@ public static class TestImages public const string Calliphora_GrayscaleDeflate_Predictor = "Tiff/Calliphora_gray_deflate_predictor.tiff"; public const string Calliphora_GrayscaleLzw_Predictor = "Tiff/Calliphora_gray_lzw_predictor.tiff"; public const string Calliphora_GrayscaleDeflate = "Tiff/Calliphora_gray_deflate.tiff"; + public const string Calliphora_GrayscaleUncompressed16Bit = "Tiff/Calliphora_grayscale_uncompressed_16bit.tiff"; + public const string Calliphora_GrayscaleDeflate_Predictor16Bit = "Tiff/Calliphora_gray_deflate_predictor_16bit.tiff"; + public const string Calliphora_GrayscaleLzw_Predictor16Bit = "Tiff/Calliphora_gray_lzw_predictor_16bit.tiff"; + public const string Calliphora_GrayscaleDeflate16Bit = "Tiff/Calliphora_gray_deflate_16bit.tiff"; public const string Calliphora_RgbDeflate_Predictor = "Tiff/Calliphora_rgb_deflate_predictor.tiff"; public const string Calliphora_RgbJpeg = "Tiff/Calliphora_rgb_jpeg.tiff"; public const string Calliphora_PaletteUncompressed = "Tiff/Calliphora_palette_uncompressed.tiff"; @@ -796,6 +802,7 @@ public static class TestImages public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff"; public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff"; + public const string GrayscaleUncompressed16Bit = "Tiff/grayscale_uncompressed_16bit.tiff"; public const string GrayscaleJpegCompressed = "Tiff/JpegCompressedGray.tiff"; public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff"; public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff"; 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/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs index 33b7228aed..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(); @@ -50,7 +54,7 @@ public class ImageDifferenceIsOverThresholdException : ImagesSimilarityException return "MacOS"; } - if (TestEnvironment.IsMacOS) + if (TestEnvironment.IsLinux) { return "Linux"; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 3285da31b4..3652d77a1e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -207,7 +207,7 @@ public abstract partial class TestImageProvider : IXunitSerializable Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); - options.Configuration = this.Configuration; + options.SetConfiguration(this.Configuration); // Used in small subset of decoder tests, no caching. // TODO: Check Path here. Why combined? @@ -244,7 +244,7 @@ public abstract partial class TestImageProvider : IXunitSerializable Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(options, nameof(options)); - options.GeneralOptions.Configuration = this.Configuration; + options.GeneralOptions.SetConfiguration(this.Configuration); // Used in small subset of decoder tests, no caching. // TODO: Check Path here. Why combined? @@ -268,7 +268,7 @@ public abstract partial class TestImageProvider : IXunitSerializable private Image DecodeImage(IImageDecoder decoder, DecoderOptions options) { - options.Configuration = this.Configuration; + options.SetConfiguration(this.Configuration); var testFile = TestFile.Create(this.FilePath); using Stream stream = new MemoryStream(testFile.Bytes); @@ -278,7 +278,7 @@ public abstract partial class TestImageProvider : IXunitSerializable private Image DecodeImage(ISpecializedImageDecoder decoder, T options) where T : class, ISpecializedDecoderOptions, new() { - options.GeneralOptions.Configuration = this.Configuration; + options.GeneralOptions.SetConfiguration(this.Configuration); var testFile = TestFile.Create(this.FilePath); using Stream stream = new MemoryStream(testFile.Bytes); 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/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; } } 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 diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index 749ece4387..d7b466d09d 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,5 +1,6 @@ { "shadowCopy": false, "methodDisplay": "method", - "diagnosticMessages": true + "diagnosticMessages": true, + "preEnumerateTheories": false } 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 diff --git a/tests/Images/Input/Jpg/issues/issue-2334-a.jpg b/tests/Images/Input/Jpg/issues/issue-2334-a.jpg new file mode 100644 index 0000000000..8f7ac362d8 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2334-a.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75d7e645cc359340a0a77f13c842551dce8f82773d2eba18bf18b149dcf9a2ff +size 411155 diff --git a/tests/Images/Input/Jpg/issues/issue-2334-b.jpg b/tests/Images/Input/Jpg/issues/issue-2334-b.jpg new file mode 100644 index 0000000000..edfc9637b8 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2334-b.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad6725bfa405454f6fb524dd632a53367f8853fd4a68705c773f0aeef068f7b3 +size 159229 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate_16bit.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate_16bit.tiff new file mode 100644 index 0000000000..3864a62444 --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate_16bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fdf62eb43c0349cb8c6ad67e5389a0f307944d8a8e760667a7f78fcc48a9ffa +size 62698 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor_16bit.tiff b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor_16bit.tiff new file mode 100644 index 0000000000..b25f2d29ea --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_deflate_predictor_16bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8040010e7f760d09dda49145050b07f32826037c11686d976b4b7949a0c40c18 +size 54086 diff --git a/tests/Images/Input/Tiff/Calliphora_gray_lzw_predictor_16bit.tiff b/tests/Images/Input/Tiff/Calliphora_gray_lzw_predictor_16bit.tiff new file mode 100644 index 0000000000..8f7e72441a --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_gray_lzw_predictor_16bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67246d3bfc0c361fae21db04fa0251168c7e12abe3c3cc134cd1d685fb09876f +size 58392 diff --git a/tests/Images/Input/Tiff/Calliphora_grayscale_uncompressed_16bit.tiff b/tests/Images/Input/Tiff/Calliphora_grayscale_uncompressed_16bit.tiff new file mode 100644 index 0000000000..62d63a124b --- /dev/null +++ b/tests/Images/Input/Tiff/Calliphora_grayscale_uncompressed_16bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42077e6e0d0e7e32bdc0dadb837aad03fd4ae9ceff158bdf213ea9b76dbc36f1 +size 119601 diff --git a/tests/Images/Input/Tiff/grayscale_uncompressed_16bit.tiff b/tests/Images/Input/Tiff/grayscale_uncompressed_16bit.tiff new file mode 100644 index 0000000000..85391d9fe0 --- /dev/null +++ b/tests/Images/Input/Tiff/grayscale_uncompressed_16bit.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49de8d78dc22c4c7e62b0c02ae409550c0247c49ec4685162a0fe986d3280aa7 +size 131294